微信小程序宿主环境

1

 

小程序宿主环境

3.1 渲染层和逻辑层

小程序的运行环境分成渲染层和逻辑层,第2章提到过 WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。

 

3.1.1 渲染“Hello World”页面

我们看看小程序是如何把脚本里边的数据渲染在界面上的。
WXML模板使用 view 标签,其子节点用 {{ }} 的语法绑定一个 msg 的变量,如代码清单3-1所示。
代码清单3-1 渲染“Hello World”WXML代码

<view>{{ msg }}</view>

在 JS 脚本使用 this.setData 方法把 msg 字段设置成 “Hello World”

 

1.渲染层和数据相关。

2.逻辑层负责产生、处理数据。

3.逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。

 

3.1.2 通信模型

小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发,

 

3.1.3 数据驱动

通常界面视图和变量状态是相关联的,如果有某种“方法”可以让状态和视图绑定在一起(状态变更时,视图也能自动变更),那我们就可以省去手动修改视图的工作。
这个方法就是“数据驱动”
WXML结构实际上等价于一棵Dom树,通过一个JS对象也可以来表达Dom树的结构

 

3.1.4 双线程下的界面渲染

小程序的逻辑层和渲染层是分开的两个线程。在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面,

 

 

3.2 程序与页面

3.2.1 程序

“小程序”指的是产品层面的程序,而“程序”指的是代码层面的程序实例,为了避免误解,下文采用App来代替代码层面的“程序”概念。

1. 程序构造器App()

宿主环境提供了 App() 构造器用来注册一个程序App,需要留意的是App() 构造器必须写在项目根目录的app.js里,App实例是单例对象,在其他JS脚本中可以使用宿主环境提供的 getApp() 来获取程序实例。

2. 程序的生命周期和打开场景

3. 小程序全局数据

 

3.2.2 页面

1. 文件构成和路径

一个页面是分三部分组成:界面、配置和逻辑。界面由WXML文件和WXSS文件来负责描述,配置由JSON文件进行描述,页面逻辑则是由JS脚本文件负责。一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的。
页面路径需要在小程序代码根目录app.json中的pages字段声明,否则这个页面不会被注册到宿主环境中。例如两个页面的文件的相对路径分别为 pages/index/page.  pages/other/other. (表示wxml/wxss/json/js四个文件),在app.jsonpages字段的代码路径需要去除.后缀,如代码清单3-7所示,默认pages字段的第一个页面路径为小程序的首页。

 

2. 页面构造器Page()

宿主环境提供了 Page() 构造器用来注册一个小程序页面,Page()在页面脚本page.js中调用,Page() 的调用方式如代码清单3-8所示。Page构造器接受一个Object参数,参数说明如表3-4所示,其中data属性是当前页面WXML模板中可以用来做数据绑定的初始数据,我们会在后文展开讨论;onLoad / onReady / onShow / onHide /onUnload 5个回调是Page实例的生命周期函数,我们在后文展开;onPullDownRefresh / onReachBottom / onShareAppMessage / onPageScroll 4个回调是页面的用户行为,我们也会在后文展开。

 

3. 页面的生命周期和打开参数

页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器参数所定义的onLoad方法会被调用,onLoad在页面没被销毁之前只会触发1次,在onLoad的回调中,可以获取当前页面所调用的打开参数option,关于打开参数我们放在这一节的最后再展开阐述。
页面显示之后,Page构造器参数所定义的onShow方法会被调用,一般从别的页面返回到当前页面时,当前页的onShow方法都会被调用。
在页面初次渲染完成时,Page构造器参数所定义的onReady方法会被调用,onReady在页面没被销毁前只会触发1次,onReady触发时,表示页面已经准备妥当,在逻辑层就可以和视图层进行交互了。
以上三个事件触发的时机是onLoad早于 onShow,onShow早于onReady。
页面不可见时,Page构造器参数所定义的onHide方法会被调用,这种情况会在使用wx.navigateTo切换到其他页面、底部tab切换时触发。
当前页面使用wx.redirectTo或wx.navigateBack返回到其他页时,当前页面会被微信客户端销毁回收,此时Page构造器参数所定义的onUnload方法会被调用。
我们可以看到,Page的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该在其他代码中主动调用Page实例的生命周期函数。
最后我们说一下页面的打开参数query,让我们来设想这样一个场景,我们实现一个购物商城的小程序,我们需要完成一个商品列表页和商品详情页,点击商品列表页的商品就可以跳转到该商品的详情页,当然我们不可能为每个商品单独去实现它的详情页。我们只需要实现一个商品详情页的pages/detail/detail.(代表WXML/WXSS/JS/JSON文件)即可,在列表页打开商品详情页时把商品的id传递过来,详情页通过刚刚说的onLoad回调的参数option就可以拿到商品id,从而绘制出对应的商品,代码

 

小程序把页面的打开路径定义成页面URL,其组成格式和网页的URL类似,在页面路径后使用英文 ? 分隔path和query部分,query部分的多个参数使用 & 进行分隔,参数的名字和值使用 key=value 的形式声明。在页面Page构造器里onLoad的option可以拿到当前页面的打开参数,其类型是一个Object,其键值对与页面URL上query键值对一一对应。和网页URL一样,页面URL上的value如果涉及特殊字符(例如:&字符、?字符、中文字符等,详情参考URI的RFC3986说明 ),需要采用UrlEncode后再拼接到页面URL上。

 

4. 页面的数据

3.1.4节讨论了小程序界面渲染的基本原理,我们知道小程序的页面结构由WXML进行描述,WXML可以通过数据绑定的语法绑定从逻辑层传递过来的数据字段,这里所说的数据其实就是来自于页面Page构造器的data字段,data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。

 

 

宿主环境所提供的Page实例的原型中有setData函数,我们可以在Page实例下的方法调用this.setData把数据传递给渲染层,从而达到更新界面的目的。由于小程序的渲染层和逻辑层分别在两个线程中运行,所以setData传递数据实际是一个异步的过程,所以setData的第二个参数是一个callback回调,在这次setData对界面渲染完毕后触发。
setData其一般调用格式是 setData(data, callback),其中data是由多个key: value构成的Object对象。
代码清单3-11 使用setData更新渲染层数据

 

 

实际在开发的时候,页面的data数据会涉及相当多的字段,你并不需要每次都将整个data字段重新设置一遍,你只需要把改变的值进行设置即可,宿主环境会自动把新改动的字段合并到渲染层对应的字段中,如下代码所示。data中的key还可以非常灵活,以数据路径的形式给出,例如 this.setData({"d[0]": 100}); this.setData({"d[1].text": 'Goodbye'}); 我们只要保持一个原则就可以提高小程序的渲染性能:每次只设置需要改变的最小单位数据。

 

此外需要注意以下3点:

  1. 直接修改 Page实例的this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
  2. 由于setData是需要两个线程的一些通信消耗,为了提高性能,每次设置的数据不应超过1024kB。
  3. 不要把data中的任意一项的value设为undefined,否则可能会有引起一些不可预料的bug。

5. 页面的用户行为

小程序宿主环境提供了四个和页面相关的用户行为回调:

  1. 下拉刷新 onPullDownRefresh
    监听用户下拉刷新事件,需要在app.json的window选项中或页面配置page.json中设置enablePullDownRefresh为true。当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新。
  2. 上拉触底 onReachBottom
    监听用户上拉触底事件。可以在app.json的window选项中或页面配置page.json中设置触发距离onReachBottomDistance。在触发距离内滑动期间,本事件只会被触发一次。
  3. 页面滚动 onPageScroll
    监听用户滑动页面事件,参数为 Object,包含 scrollTop 字段,表示页面在垂直方向已滚动的距离(单位px)。
  4. 用户转发 onShareAppMessage
    只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮,在用户点击转发按钮的时候会调用,此事件需要return一个Object,包含title和path两个字段,用于自定义转发内容,如代码清单3-13所示。
    代码清单3-13 使用onShareAppMessage自定义转发字段
     

6. 页面跳转和路由

一个小程序拥有多个页面,我们可以通过wx.navigateTo推入一个新的页面,如图3-6所示,在首页使用2次wx.navigateTo后,页面层级会有三层,我们把这样的一个页面层级称为页面栈。

 

 

后续为了表述方便,我们采用这样的方式进行描述页面栈:[ pageA, pageB, pageC ],其中pageA在最底下,pageC在最顶上,也就是用户所看到的界面,需要注意在本书编写的时候,小程序宿主环境限制了这个页面栈的最大层级为10层 ,也就是当页面栈到达10层之后就没有办法再推入新的页面了。我们下面来通过上边这个页面栈描述以下几个和导航相关的API。

使用 wx.navigateTo({ url: 'pageD' }) 可以往当前页面栈多推入一个 pageD,此时页面栈变成 [ pageA, pageB, pageC, pageD ]。

使用 wx.navigateBack() 可以退出当前页面栈的最顶上页面,此时页面栈变成 [ pageA, pageB, pageC ]。

使用wx.redirectTo({ url: 'pageE' }) 是替换当前页变成pageE,此时页面栈变成 [ pageA, pageB, pageE ],当页面栈到达10层没法再新增的时候,往往就是使用redirectTo这个API进行页面跳转。

小程序提供了原生的Tabbar支持,我们可以在app.json声明tabBar字段来定义Tabbar页(注:更多详细参数见Tabbar官方文档 )。

 

我们可以在刚刚的例子所在的页面栈中使用wx.switchTab({ url: 'pageF' }),此时原来的页面栈会被清空(除了已经声明为Tabbar页pageA外其他页面会被销毁),然后会切到pageF所在的tab页面,页面栈变成 [ pageF ],此时点击Tab1切回到pageA时,pageA不会再触发onLoad,因为pageA没有被销毁。

补充一下,wx.navigateTo和wx.redirectTo只能打开非TabBar页面,wx.switchTab只能打开Tabbar页面。

我们还可以使用 wx. reLaunch({ url: 'pageH' }) 重启小程序,并且打开pageH,此时页面栈为 [ pageH ]。表3-5罗列了详细的页面路由触发方式及页面生命周期函数的对应关系。

 

Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C 是从 A 页面打开的页面,D 页面是从 C 页面打开的页面为例)如表3-6所示,注意Tabbar页面初始化之后不会被销毁。

 

3.3 组件

一个小程序页面可以分解成多个部分组成,组件就是小程序页面的基本组成单元。为了让开发者可以快速进行开发,小程序的宿主环境提供了一系列基础组件。
组件是在WXML模板文件声明中使用的,WXML的语法和HTML语法相似,小程序使用标签名来引用一个组件,通常包含开始标签和结束标签,该标签的属性用来描述该组件。
代码清单3-15 在WXML使用组件示例

<!-- page.wxml -->

<image mode="scaleToFill" src="img.png"></image>

需要注意,所有组件名和属性都是小写,多个单词会以英文横杠 "-" 进行连接。
对于一些容器组件,其内容可以声明在其开始标签和结束标签之间。

 

3.4 API

宿主环境提供了丰富的API,可以很方便调起微信提供的能力。在前文的代码示例中可以看到,wx.navigateTo可以保留当前页面,然后跳转到新的页面。这里的wx对象实际上就是小程序的宿主环境所提供的全局对象,几乎所有小程序的API都挂载在wx对象底下(除了Page/App等特殊的构造器),所以本书谈到API概念时,通常指的是wx对象底下的方法。
小程序提供的API按照功能主要分为几大类:网络、媒体、文件、数据缓存、位置、设备、界面、界面节点信息还有一些特殊的开放接口,我们介绍一下API一般调用的约定:

  1. wx.on* 开头的 API 是监听某个事件发生的API接口,接受一个 Callback 函数作为参数。当该事件触发时,会调用 Callback 函数。
  2. 如未特殊约定,多数 API 接口为异步接口 ,都接受一个Object作为参数。
  3. API的Object参数一般由success、fail、complete三个回调来接收接口调用结果,示例代码如代码清单3-17所示,详细说明如表3-9所示。
  4. wx.get* 开头的API是获取宿主环境数据的接口。
  5. wx.set* 开头的API是写入数据到宿主环境的接口。

还有需要注意到API调用大多都是异步的,其次,有部分API会拉起微信的原生界面,此时会触发Page的onHide方法,当用户从原生界面返回到小程序时,会触发Page的onShow方法。

API的数量非常多,而且随着宿主环境的迭代更新会持续新增API,在这里我们并不想一一展开叙述每一个API的含义,开发者只要了解一般调用API的技巧,再通过官方API文档 https://mp.weixin.qq.com/debug/wxadoc/dev/api/了解到对应的API参数细节即可。在第四章中我们还会通过各种应用场景来展示很多API的用法。

 

3.5 事件

3.5.1 什么是事件

UI界面的程序需要和用户互动,例如用户可能会点击你界面上某个按钮,又或者长按某个区域,这类反馈应该通知给开发者的逻辑层,需要将对应的处理状态呈现给用户。
有些时候程序上的“行为反馈”不一定是用户主动触发的,例如我们在视频video播放的过程中,播放进度是会一直变化的,这种反馈也应该通知给开发者做相应的逻辑处理。
在小程序里边,我们把这种“用户在渲染层的行为反馈”以及“组件的部分状态反馈”抽象为渲染层传递给逻辑层的“事件”,如图3-7所示。

 

 

3.5.2 事件类型和事件对象

前边说到触发事件是由“用户在渲染层的行为反馈”以及“组件的部分状态反馈”引起的,由于不同组件的状态不一致,所以我们这里不讨论组件相关的事件(组件的事件可以参考其参数说明,详情见官方文档 https://mp.weixin.qq.com/debug/wxadoc/dev/component/ )

 

 

当事件回调触发的时候,会收到一个事件对象

 

这里需要注意的是target和currentTarget的区别,currentTarget为当前事件所绑定的组件,而target则是触发该事件的源头组件。

 

3.5.3 事件绑定与冒泡捕获

事件绑定的写法和组件属性一致,以key="value"的形式,其中:

  1. key以bind或者catch开头,然后跟上事件的类型,如bindtap、catchtouchstart。自基础库版本1.5.0起,bind和catch后可以紧跟一个冒号,其含义不变,如bind:tap、catch:touchstart。同时bind和catch前还可以加上capture-来表示捕获阶段。
  2. value是一个字符串,需要在对应的页面Page构造器中定义同名的函数,否则触发事件时在控制台会有报错信息。
    bind和capture-bind的含义分别代表事件的冒泡阶段和捕获阶段,

 

 

3.6 兼容

小程序的宿主环境一直在迭代更新,提供更多的能力给开发者去完成更多的事情,所以你的小程序会运行在不同版本的宿主环境下。为了让你的小程序在不同环境下都能提供相应的服务,我们需要来了解一下在小程序中如何实现兼容办法。
我们可能需要针对不同手机进行程序上的兼容,此时可以使用 wx.getSystemInfo 或者 wx.getSystemInfoSync 来获取手机品牌、操作系统版本号、微信版本号以及小程序基础库版本号等,通过这个信息,我们可以针对不同平台做差异化的服务。

 

随着宿主环境的更新,新版本的宿主环境会提供一些新的API,你可以通过判断此API是否存在来做程序上的兼容。

 

我们可以选择合适的判断方法来做小程序的向前兼容,以保证我们的小程序在旧版本的微信客户端也能工作正常。在不得已的情况下(小程序强依赖某个新的API或者组件时),还可以通过在小程序管理后台设置“基础库最低版本设置”来达到不向前兼容的目的。例如你选择设置你的小程序只支持1.5.0版本以上的宿主环境,那么当运行着1.4.0版本宿主环境的微信用户打开你的小程序的时候,微信客户端会显示当前小程序不可用,并且提示用户应该去升级微信客户端。

 

3.7 本章小结

在本章中,我们介绍了小程序宿主环境的基本运行机制以及它所提供的各种能力,组合这些能力可以完成一个体验非常流畅的小程序,同时读者也了解到如何对小程序在不同环境下兼容的办法,以便给不同环境下的的微信用户提供可靠或者降级的服务。在下一章中,我们会给读者展示如何利用小程序的能力实现各类场景应用。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值