微信小程序的火爆相信不用多说,作为一个前端,小程序几乎可以算得上是前端技能的一个加分项,如果找工作的时候会小程序,那么相对而言会有很多优势;
官方解释:小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。简单的说,小程序就像是微信的一个功能插件,这个功能插件可以无缝的接入微信,让用户在微信中使用小程序时获得接近原生app的体验;
官方地址:小程序开发者文档,通过阅读简介,对于小程序其实可以理解成,微信为了更好的为用户提供使用体验,它对JavaScript进行了再一次的封装,并且制定了许多api供开发者使用,虽然经过封装,但是开发语言的本质是JavaScript改变不了,因此,对前端开发者而言学习成本最小;
注意的是,小程序的开发环境和网页开发环境是有区别的,虽然两者几乎一样,但是还是有一些区别,比如jQuery等前端库没有办法使用,环境也于Node.js不尽相同,因此有一些NPM的包也无法运行;
小程序不是跟web项目那种虽然本地创建的,需要到官网进行注册,开发者工具也是由微信官方提供(开发者工具下载),注册过程就不说了,我第一次注册的时候还是比较顺利的,值的说一下的就是,每个有些只能申请一个小程序,当注册成功之后,在菜单栏“开发” -> “开发设置” -> 可以看到一个属性,叫做AppID(小程序ID),这个很重要,记得保存到本地,小程序的AppID相当于身份证;
之后,打开安装完毕的开发者工具,扫码登录,如果是多人协同项目,那么需要在小程序后台(也就是上面注册后获取AppID的那个后台),在开发人员中添加多人微信号就可以了;之后新建一个小程序项目还是导入小程序项目都随用户选择;
关于开发者工具的具体介绍,可以看一下官方文档,还是很有必要的:开发者工具介绍;如果需要预览项目,那么点击开发者工具上面的“预览”按钮,就可以扫码预览;
完成之后,可以看到一个类似于下例的界面:
要开发项目,那么肯定得先了解小程序的目录结构,所以请先看下以下示例,这是一个新建项目后的基本目录: 以app开头的三个文件,是这个小程序的全局文件,可以这么理解,这个三个文件会运行在小程序启动之后,在用户看到页面之前的这段时间内,小程序根据这三个配置文件决定该让用户先看到哪个页面,看到页面前是否需要进行某些特定的操作,是不是需要有tab栏等等问题:
app.js:全局小程序逻辑文件,这个文件里写的代码是会影响到整个小程序的,该文件是必须的,不能没有,每一次小程序打开都会读取这个文件内的逻辑;app.json:全局小程序公共配置文件,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。完整配置项说明请参考小程序全局配置,这个文件也是必须的,不能没有;app.wxss:全局的公共样式表,这个不是必须的,因为如果不怕麻烦,所有相同的样式都写在页面内部的wxss文件里,也不是不可以;页面是写在pages这个文件夹里面的,下面每一个文件夹都是一个页面,每个页面里都包含了: .js文件, .json文件, .wxml文件, .wxss文件;
.js: 该页面的页面逻辑,这个文件里的逻辑只针对当前页面,不会影响到别的页面;.json:该文件的页面配置文件,影响范围也仅限当前页面;.wxml:该页面的布局文件,相当于html文件,描述的是页面的结构;.wxss:该页面的样式表,相当于css,描述的是当前页面的样式;通过上一节知道,小程序的每个页面基本是由: .js文件, .json文件, .wxml文件, .wxss文件,这些文件构成;
json是一种数据格式,并不是编程语言,在小程序中扮演静态配置角色,相当于告诉小程序,我需要配置这些指定的东西,比如每次打开小程序,首先需要显示的页面是page/index/index这个页面,比如所有小程序页面中的导航栏都是黑色的,字体颜色是白色的;
小程序中有三种json文件,分别是:项目的根目录有一个 app.json 和 project.config.json,以及每个页面文件夹下的json文件(比如上图中的logs文件夹下的logs.json);
是当前小程序的全局配置,包括了小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等。默认的配置内容大致如下:
{ "pages":[ "pages/index/index", "pages/logs/logs" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "WeChat", "navigationBarTextStyle":"black" } } pages属性是告诉小程序所有的页面路径,其中第一个值是打开小程序时,默认显示的页面,如果将上例中的logs移动到第一个,那么打开小程序时首先显示的界面就是logs中的页面,不再是index;window是定义小程序中所有页面的顶部背景色,文字颜色等等;其他配置项细节可以参考文档 小程序的配置 app.json ;
这个配置文件是针对开发者的,因为每个开发者都会有一点属于自己个性的东西,此时如果换一台电脑,那么这些配置就需要重新配置,因此,小程序通过这个文件将配置好的备份,换电脑的时候不再需要重新配置,开发者工具会自动根据这个文件将个性化配置恢复到设定的状态;
这个page.json代表的是每个页面文件夹下的json文件,比如上图中的logs.json,index.json等,在app.json中定义的配置是全局的配置,比如所有页面的导航都是黑色;
但实际项目中不可能是这样的,设计师不会给你一套设计,所有的导航都是白色,肯定是有的页面是白色的,有的页面是主色的,因此,在当前页面有一些特殊的配置需要覆盖全局配置时,那么就可以在这个当前的配置文件中配置,在这里配置的信息的优先级会高于全局配置,因此等到加载页面的时候会将这个里面的配置信息为优先;
WXML文件,可以和HTML的关系划约等于,但相比之下可能和vue中的vue文件更加类似,微信将常用的一些标签,功能进行了封装,变成了组件,等到使用的时候直接调用就可以了,而数组的使用则使用双花括号的方式进行绑定,没错和vue中的使用方式一样,因此,如果你很熟悉vue,那么对小程序的入门可以非常快
<view class="container"> <view class="userinfo"> <button wx:if="{{!hasUserInfo && canIUse}}"> 获取头像昵称 </button> <block wx:else> <image src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </block> </view> <view class="usermotto"> <text class="user-motto">{{motto}}</text> </view> </view>这是一个官网的例子,简单分析一下,上面有view,button这些微信封装的组件,使用方式和html几乎一致,区别在于,在html中是通过div,header,footer这些标签进行布局的,在小程序中则不是,通通使用view进行布局,嗯,没错,这个view相当于H5之前页面布局的div标签;
标签上有 wx:if 这样的属性,这个相当于vue中的v-if,所以小程序其实也是MVVM的开发模式,与vue不同的是,vue中的指令是通过v-来表达的,而在微信小程序中的指令是通过wx:开头表达的;
WXSS样式基本等同于CSS,区别在于:
新增了尺寸单位rpx,通过使用这个单位,在不同屏幕上的换算交给小程序即可,不再需要自己去做适配;同样WXSS也有全局样式和局部样式,同样的,相同的部分布局的WXSS样式会覆盖全局样式;对于CSS选择器,WXSS仅支持部分,比如后台选择器,兄弟选择器等是不支持的,具体请看这篇更详细的文档 WXSS ;rpx是微信小程序官方提供的一个单位,这是一个会自适应屏幕的单位,宽度100%的时候是750rpx,通过这个可以帮助我们更好的布局页面(没办法安卓的屏幕类型实在是多)
JS交互部分就是前端开发人员的JavaScript,使用方式基本是一致的,区别在于事件的名字可能不大一样,当然本质上是没有什么区别,其作用就是和用户做交互,比如:响应用户的点击、获取用户的位置等等,下面是一个示例,点击事件:
<view>{{ msg }}</view> <button bindtap="clickMe">点击我</button>通过点击按钮,希望将msg的值显示成Hello World,于是声明了一个bindtap属性,其值是一个函数,这个属性代表单击后会触发后面绑定的函数
Page({ clickMe: function() { this.setData({ msg: "Hello World" }) } })其实可以这么理解,这个button不是我们html中的button,原生的button上的功能太少,达不到实际开发中的需求,为了提高效率,微信中会将这些常用的标签都进行一次封装,大大的减少了耗时,换句话说,这里的button实际上类似于Vue的组件,上面的属性bindtap,其实是上传到父组件上的一个属性,这个属性对应的函数会传递到组件内部的click事件中;
具体的事件相关请看参考文档 WXML - 事件;
小程序中的数据绑定采用也是“Mustache”语法,也就是双花括号,在双花括号内,可以进行简单的运算,比如:三元运算,算数运算,逻辑判断,字符串运算等等,也就是说,在双花括号内的运算,小程序会进行元算,之后将结果呈现出来;
微信页面上的数据都是存储在js文件Page中的data方法中的,类似于vue中的数据也是存储在js部分的data中的,因此,比如:
//index.wxml文件中用双花括号引入变量message <view class='body-main'> {{message}} </view> //在index.js文件中的data属性中定义变量message并赋值 Page({ /** * 页面的初始数据 */ data: { message: "你好" }, })这时候,就可以在左侧的界面上显示对应的文字“你好”,同理如果是,获取数组,或者对象,获取方式也一样:
//index.wxml文件中用双花括号引入变量message <view class='body-main'> {{person.age}}{{list[0]}} </view> //在index.js文件中的data属性中定义变量message并赋值 Page({ /** * 页面的初始数据 */ data: { person:{ age:18 }, list:["你好"] }, })另外小程序的变量是可以直接在属性上使用的,比如有一个标签,上面有id属性,id属性的某一部分是data中的变量,同样也是在需要的地方使用双花括号引入
//index.wxml <view class='body-main'> {{message}} <ul> <li id="id-{{id}}"></li> </ul> </view> //index.js Page({ /** * 页面的初始数据 */ data: { message: "你好", id:"item" }, })同样,双花括号的写法也可以写在指令里面,比如:
//index.wxml //指令wx:if这些个是渲染条件,具体看下一节,这里看个大概就好 <view wx:if="{{code===1}}">{{message}}1</view> <view wx:elif="{{code===2}}">{{message}}2</view> <view wx:else>{{message}}3</view> //index.js Page({ /** * 页面的初始数据 */ data: { code:1 }, })这样,如果code的值为1,那么第一个view后面的值就是ture,那么此时页面上就会渲染第一个view,剩下的就不再执行,同理code为2的时候,只有第二个后面是true,那么只会渲染第二个view;
小程序中的列表渲染使用的指令是:wx:for,用法几乎和vue中的一样,先看个例子:假设现在从服务器请求到了一个数组,数组中每一项都是一个对象,其中有一个id值,需要绑定在列表上的每个id属性,并且将值打印出来
<!--index.wxml--> <ul> <li wx:for="{{array}}" id="id-{{item.id}}">{{item.id}}-{{index}}</li> </ul> //index.js Page({ data: { array:[ { id:"item1" }, { id:"item2" }, { id:"item3" } ] } })通过双花括号,可以将data中的数组array获取到,之后通过wx:for的指令,将数组进行遍历,之后变量item代表对应的每一项,index对应每一项的下标值;
注意的是,为了保证列表改变时,每一项都可以被小程序内部追踪到,因此需要绑定一个key值,比如上例,将id值绑定给key
<!--index.wxml--> <ul> <li wx:for="{{array}}" id="id-{{item.id}}" wx:key="id">{{item.id}}-{{index}}</li> </ul>key的绑定不需要双花括号,也不需要item.之类的操作,其中的的变量都是每一项对象内的的键值;另外,假设获得的数组,每一项就是一个数字或者字符串,此时没有对应的属性名,那么可以使用保留字this,这个this就是每一项本身,比如:
<!--index.wxml--> <ul> <li wx:for="{{array}}" wx:key="*this">{{item}}</li> </ul> //index.js Page({ data: { array:[1, 2, 3] } })小程序中的条件渲染使用的指令是wx:if,wx:elif,wx:else,用法和vue基本一致,看个示例:
//指令wx:if <view wx:if="{{code===1}}">{{message}}1</view> <view wx:elif="{{code===2}}">{{message}}2</view> <view wx:else>{{message}}3</view> //index.js Page({ /** * 页面的初始数据 */ data: { code:1 }, })wx:if将判断=号后面的值是否为true,只有值为true的时候才会渲染对应的部分,注意的是wx:if是真的渲染不渲染,假如为false,那么该部分是不会被渲染进DOM树的,如果要使用的是类似于v-show这种渲染进dom树,但是不显示,则使用指令:hidden
<view hidden="{{code===1}}">{{message}}1</view>此时的view不管hidden后面的是true还是false,都会将该部分渲染进DOM中,只是true的时候是显示的,false的时候是隐藏的;
如果在项目中的某一部分是需要经常的切换显示隐藏,那么使用hidden比较好,如果不是太频繁,那么使用wx:if比较好;
模版,template,可以理解成一段可重复使用的代码片段,这些代码片段只需要定义一次,就可以不断的重复使用,比如:
//index.wxml <template name="staffName"> <view> FirstName: {{firstName}}, LastName: {{lastName}} </view> </template>通过template定义了一个模版,并且给这个模版命名了一个名字staffName,使用的使用只需要通过名字就可以指定需要使用哪个模版了
//index.wxml <template is="staffName" data="{{...person}}"></template> <template is="staffName" data="{{...person}}"></template> <template is="staffName" data="{{...person}}"></template> //index.js Page({ data: { person:{ name:"oliver", age:18 } } })这里模版被使用了3次,通过is属性,指定要使用模版的名字,通过data属性注入数据,当然,模版的名字也是支持双花括号语法的,比如
<template name="{{name}}"> <view> FirstName: {{firstName}}, LastName: {{lastName}} </view> </template>通常,如果项目中有好些相同的布局,那么就可以考虑抽离出来,单独存放,然后项目中往往是组合使用,比如使用的时候通过条件渲染模版,当获取到的状态是1的时候渲染的是模版1,如果状态是2的时候渲染的是模版2等等;
这里又出现了一个新问题,如果模版在页面a中,那么在这么在页面b中也使用这个模版?答案是通过import或include引入;
import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template,举个例子:C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template; **
和import不同,import会引入目标文件中的template,而include则是会引入 、外的所有代码,也就是说,会将整个页面都拷贝过来了;
比如现在所有的页面都需要使用相同的header和相同的footer,那么就可以将header和footer单独写一个文件,其他所有页面都使用include引入
<!-- index.wxml --> <include src="header.wxml"/> <view> body </view> <include src="footer.wxml"/> <!-- header.wxml --> <view> header </view> <!-- footer.wxml --> <view> footer </view>WXS是小程序的一套脚本语言,它的语法和JavaScript不完全一致,WXS代码可以编写在wxml的标签中,也可以写在以.wxs为后缀的文件内;
为什么要有WXS,作用上看它和JavaScript几乎一致,官网上写到:由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。这个速度差异其实很明显,尤其是项目比较大的时候;
每个.wxs文件和标签都是一个独立的模块;都有其自己独立的作用域,每一个模块内的变量与函数,默认都是私有的,这就代表如果要被其他模块使用,必须通过module.exports实现,看个例子
<!--wxml--> <wxs module="m1"> var msg = "hello world"; module.exports.message = msg; </wxs> <view> {{m1.message}} </view>这个是一个页面渲染的例子,基本上就是如果要使用wxs内的变量,必须通过module.exports导出,否则就无法识别,再看一个数据处理的例子:
// index.js Page({ data: { array: [1, 2, 3, 4, 5, 1, 2, 3, 4] } }) //index.wxml <!-- 下面的 getMax 函数,接受一个数组,且返回数组中最大的元素的值 --> <wxs module="m1"> var getMax = function(array) { var max = undefined; for (var i = 0; i < array.length; ++i) { max = max === undefined ? array[i] : (max >= array[i] ? max : array[i]); } return max; } module.exports.getMax = getMax; </wxs> // 调用 wxs 里面的 getMax 函数,参数为 index.js 里面的 array <view> {{m1.getMax(array)}} </view>同样,如果是新建了一个wxs文件
var name = "我的名字是Oliver"; var bar = function(d){ return d; } module.exports = { name:name, func:bar }那么在wxml文件中需要先指明路径,比如:
//wxml文件 //通过src属性引入wxs文件,然后通过module命名模块 <wxs src="./indexDemo.wxs" module="m2"></wxs> <span>{{m2.name}}</span> <span>{{m2.func("大家好")}}</span>另外,如果现在有两个wxs文件,分别为a.wxs和b.wxs,如果要在b.wxs中引用a.wxs,那么可以通过require引入,比如:
//a.wxs var foo = "'hello world' from tools.wxs"; var bar = function (d) { return d; } module.exports = { FOO: foo, bar: bar, }; module.exports.msg = "some msg"; //b.wxs var tools = require("./a.wxs"); console.log(tools.FOO); console.log(tools.bar("logic.wxs")); console.log(tools.msg);注意的是,require后面的路径必须是相对路径,另外如果多个wxs文件的module重名了,那么写在后面的将覆盖前面的;
小程序的事件系统和JS的事件系统的用法基本是一致的,区别在于事件名不一样,因此具体的事件相关请看参考文档 WXML - 事件; 先看个简单的例子:
<view bindtap="tapName"> Click me! </view> //错误示例 <view bindtap="tapName("oliver")"> Click me! </view> 这种传递参数是错误的,微信小程序不支持这种方式传递参数这里view标签上通过bindtap绑定了一个tapName事件,作用是当用户点击到当前这个view时就会触发tapName函数,而tapName函数则写在对应的js文件中
Page({ /** * 页面的初始数据 */ data: { message: "你好", id:"adb", person:{ name:"oliver", age:18 } }, tapName: function(event) { console.log(event) } })同样,小程序中的事件分为冒泡事件和非冒泡事件,冒泡事件简单的说,就是组件上的事件被触发以后,会向其父节点传递,举个例子
<view bindtap="tapName"> <button bindtap="click">Click me!</button> </view>点击了button上的bindtap触发click函数之后,其父元素伤的bindtap同样会被触发,这就是冒泡,小程序中的冒泡事件大致有以下: 看到这里可能有疑惑,为什么事件类型中没有bind这个词,上面例子中明明是用的bindtap,表里面为什么仅仅是tap?实际上bind是一个修饰词,可以理解成是类似于bue中的v-on,v-bind这种,在小程序中bind代表的是普通事件绑定,换个可以更好理解的写法,bind:,具体示例:
<view bind:tap="tapName"> Click me! </view>通过bind修饰的事件,就是普通事件,普通事件不会对事件进行处理,但处理事件的函数名必须是字符串,因此,传参,如下例是不行的,因为小程序会将后面整体视作是一个字符串
<view bindtap="handleTap(1)"> Click here! </view>通过catch修饰事件,通过catch修饰的事件,会阻止其冒泡行为
<view catchtap="handleTap"> Click here! </view>通过mut-bind修饰的事件,代表互斥事件,一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发;
<view id="outer" mut-bind:tap="handleTap1"> outer view <view id="middle" bind:tap="handleTap2"> middle view <view id="inner" mut-bind:tap="handleTap3"> inner view </view> </view> </view>上例中,函数handleTap3被触发后,事件冒泡到父节点,之后handleTap2会被触发,再之后事件继续冒泡到父节点,但是此时的outer上的绑定也是互斥事件,因此handleTap1将不会被触发;
捕获阶段在冒泡阶段之前,采用的修饰是capture-bind和capture-catch, capture-bind:代表当组件处于捕获阶段时监听事件并触发相关函数,之后按正常的捕获和冒泡流程走; capture-catch:和capture-bind不同,capture-catch在触发事件后,会取消之后的捕获和冒泡流程,相当于直接终止了之后的流程;
<view id="outer" bind:touchstart="handleTap1" capture-bind:touchstart="handleTap2"> outer view <view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4"> inner view </view> </view>这段代码中,如果是capture-bind,那么所有的事件都会被触发,并且触发的流程是:handleTap2、handleTap4、handleTap3、handleTap1; 但如果将capture-bind替换成capture-catch,在触发第一个touchstart后,它就会终止之后的所有流程,因此,只会触发一个handleTap2函数;
理解起来和vue的生命周期有点类似,简单的说,就是在小程序在运行的过程中,会经历过不同的阶段,在不同的阶段会抛出一些函数,称为钩子函数,这些钩子函数中的代码会在这个阶段中被运行,因此在不同的阶段可以做一些特定的处理,比如获取服务器信息,比如初始化数据等等,而这些钩子函数统称为生命周期,因为运行这些钩子函数的阶段囊括了从小程序的创建到销毁;
小程序的生命周期分为两种,一种是小程序本体的,另外一种是各个页面的
小程序的生命周期是写在app.js中的
//app.js App({ //监听小程序初始化 onLaunch: function () {}, //监听小程序启动或切前台,小程序在后台切到前台的时候会调用; onShow: function(){}, //监听小程序切后台,小程序从前台切到后台的时候会调用; onHide: function(){}, /* * 发生错误是调用函数,在这个函数内,通常是用在监听线上小程序有没有发生错误的, * 当发生错误时,触发这个函数,在这个函数内可以将错误提交到后台; */ onError: function(){}, /* * 页面不存在时调用函数,比如,有时候印刷了一些小程序二维码做推广,但是因为一些特殊原因, * 页面被删除了,那么此时就可以通过这个函数监听错误,提交后台,然后做一些兼容; */ onPageNotFound: function(){}, globalData: { userInfo: null } })这个globalData,这个属性是定义的一些全局属性,比如示例中的userInfo是存储的用户信息,比如在打开小程序后,获取当前用户的信息,赋值给userInfo,那么在之后的页面显示中都可以获取到当前的用户信息了;
//index.js等其他页面 var app = getApp() Page({ /** * 页面的初始数据 */ data: { message: "你好", id:"adb", person:{ name:"oliver", age:18 } }, //打印 tapName: function(event) { console.log(app.globalData.userInfo) } })小程序的每个页面都有自己的生命周期,它的接收一个对象作为参数,其上有页面的初始化数据,生命周期回调,事件处理函数
Page({ /** * 页面的初始数据 */ data: {}, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) {}, /** * 生命周期函数--监听页面初次渲染完成,第一次打开页面完成之后触发 */ onReady: function () {}, /** * 生命周期函数--监听页面显示 * 如果小程序主体和页面都有onShow事件,那么会先触发小程序的,再触发页面的 */ onShow: function () {}, /** * 生命周期函数--监听页面隐藏, * 如果小程序主体和页面都有onHide事件,那么会先触发页面的,再触发小程序的 */ onHide: function () {}, /** * 生命周期函数--监听页面卸载,比如打开页面后路由地址被重定向了,那么此时会触发这个函数 */ onUnload: function () {}, /** * 页面相关事件处理函数--监听用户下拉动作,当用户在顶部有下拉操作的时候,触发该函数 * 比如下拉刷新操作 */ onPullDownRefresh: function () {}, /** * 页面上拉触底事件的处理函数,当页面上的数据够多,有滚动条时,如果此时滚动条拉到底部 * 会触发该函数,默认是距离底部50px时触发 */ onReachBottom: function () {}, /** * 用户点击右上角分享,点击的是右上角的三个点按钮 */ onShareAppMessage: function () {} /** * 页面滚动触发事件的处理函数 */ onPageScroll: function () {} /** * 页面尺寸改变时触发 * 比如横屏竖屏切换的时候触发 */ onResize: function () {} /** * 当前是tab页时,点击tab触发 * 比如实现点击tab栏的时候触发刷新页面操作 */ onTabItemTap: function () {} })具体的生命周期函数运行的顺序如下:
小程序启动后,界面会被展示给用户,此时的小程序处于前台状态; 当用户点击右上角胶囊按钮关闭小程序,或者按了Home键离开微信时,小程序并没有完全终止运行,而是进入了后台状态,此时的小程序还可以运行一段时间; 当再次打开小程序时,小程序又会从后台进入前台,但如果用户很久没有再进入小程序,或者系统资源紧张,小程序可能被销毁,即完全终止运行。
小程序启动可以分为两种情况,一种是冷启动,一种是热启动。
冷启动:如果用户首次打开,或小程序销毁后被用户再次打开,此时小程序需要重新加载启动,即冷启动。热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态,这个过程就是热启动。通常,只有当小程序进入后台一定时间,或者系统资源占用过高,才会被销毁。具体而言包括以下几种情形
当小程序进入后台,可以维持一小段时间的运行状态,如果这段时间内都未进入前台,小程序会被销毁。当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。具体请看运行机制这一页,大概意思就是,当小程序被启动的时候的场景,大致分为两类:A场景和B场景,打开小程序首页的场景称为A场景(具体的场景ID值看网页),打开小程序指定的某个页面,称为B场景;
在小程序的onLaunch和onShow方法参数中可获取场景值,可以根据不同的场景值对界面做一些特殊处理
App({ onLaunch: function (options) { console.log("[onLaunch] 本次场景值:", options.scene) }, onShow: function (options) { console.log("[onShow] 本次场景值:", options.scene) } })