微信小程序开发——第3章 理解小程序宿主环境

微信小程序学习总结

3.1 渲染层和逻辑层

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

3.1.1 渲染“Hello World”页面

WXML模板使用 view 标签,其子节点用 {{ }} 的语法绑定一个 msg 的变量:

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

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

Page({
  onLoad: function () {
    this.setData({ msg: 'Hello World' })
  }
})
  1. 渲染层和数据相关。
  2. 逻辑层负责产生、处理数据。
  3. 逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。

3.1.2 通信模型

小程序的渲染层和逻辑层分别由2个线程管理。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发。
在这里插入图片描述

3.1.3 数据驱动

可以让状态和视图绑定在一起(状态变更时,视图也能自动变更),则可以省去手动修改视图的工作

3.1.4 双线程下的界面渲染

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

3.2 程序与页面

从逻辑组成来说,一个小程序是由多个“页面”组成的“程序”

3.2.1 程序

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

1.程序构造器App()

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

// other.js
var appInstance = getApp()

App构造器:

App({
  onLaunch: function(options) {},
  onShow: function(options) {},
  onHide: function() {},
  onError: function(msg) {},
  globalData: 'I am global data'
})

App构造器接受一个Object参数,参数说明见下表,其中onLaunch / onShow / onHide 三个回调是App实例的生命周期函数.

参数属性 类型 描述
onLaunch Function 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
onShow Function 当小程序启动,或从后台进入前台显示,会触发 onShow
onHide Function 当小程序从前台进入后台,会触发 onHide
onError Function 当小程序发生脚本错误,或者 API 调用失败时,会触发 onError 并带上错误信息
其他字段 任意 可以添加任意的函数或数据到 Object 参数中,在App实例回调用 this 可以访问
2. 程序的生命周期和打开场景

初次进入小程序时,微信客户端初始化宿主环境,同时从网络下载或者从本地缓存中拿到小程序的代码包,将其注入到宿主环境;初始化后,微信客户端给App实例派发onLaunch事件,App构造器参数定义的onLaunch方法被调用。
进入小程序后,点击右上角的关闭,或者按Home键离开小程序,此时小程序并没有被销毁,称这种情况为“小程序进入后台状态”,App构造器参数定义的onHide方法会被调用。
当再次回到微信或者再次打开小程序时,微信客户端会把“后台”的小程序唤醒,称这种情况为“小程序进入前台状态”,App构造器参数定义的onShow方法会被调用。
App的生命周期是由微信客户端根据用户操作主动触发的
onLaunch和onShow带参数

App({
  onLaunch: function(options) { console.log(options) },
  onShow: function(options) { console.log(options) }
})

具体参数类型及其描述见文档

3. 小程序全局数据

小程序的JS脚本是运行在JsCore的线程里,小程序的每个页面各自有一个WebView线程进行渲染,所以小程序切换页面时,小程序逻辑层的JS脚本运行上下文依旧在同一个JsCore线程中。
不同页面直接可以通过App实例下的属性来共享数据。App构造器可以传递其他参数作为全局属性以达到全局共享数据的目的。
小程序全局共享数据:

// app.js
App({
  globalData: 'I am global data' // 全局共享数据
})
// 其他页面脚本other.js
var appInstance = getApp()
console.log(appInstance.globalData) // 输出: I am global data

所有页面的脚本逻辑都跑在同一个JsCore线程,页面使用setTimeout或者setInterval的定时器,然后跳转到其他页面时,这些定时器并没有被清除,需要开发者自己在页面离开的时候进行清理。

3.2.2 页面

1. 文件构成和路径

一个页面是分三部分组成:界面、配置和逻辑。界面由WXML文件和WXSS文件来负责描述,配置由JSON文件进行描述,页面逻辑则是由JS脚本文件负责。一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的。
页面路径需要在小程序代码根目录app.json中的pages字段声明,否则这个页面不会被注册到宿主环境中。
为了叙述方便,下文使用page.wxml / page.wxss / page.json / page.js 来分别代表特定页面的4个文件。

2. 页面构造器Page()

宿主环境提供了 Page() 构造器用来注册一个小程序页面,Page()在页面脚本page.js中调用,Page() 的调用方式如下:

Page({
  data: { text: "This is page data." },
  onLoad: function(options) { },
  onReady: function() { },
  onShow: function() { },
  onHide: function() { },
  onUnload: function() { },
  onPullDownRefresh: function() { },
  onReachBottom: function() { },
  onShareAppMessage: function () { },
  onPageScroll: function() { }
})

Page构造器接受一个Object参数,参数说明见下表:

参数属性 类型 描述
data Object 页面的初始数据
onLoad Function 生命周期函数--监听页面加载,触发时机早于onShow和onReady
onReady Function 生命周期函数--监听页面初次渲染完成
onShow Function 生命周期函数--监听页面显示,触发事件早于onReady
onHide Function 生命周期函数--监听页面隐藏
onUnload Function 生命周期函数--监听页面卸载
onPullDownRefresh Function 页面相关事件处理函数--监听用户下拉动作
onHide Function 生命周期函数--监听页面隐藏
onReachBottom Function 页面上拉触底事件的处理函数
onShareAppMessage Function 用户点击右上角转发
onPageScroll Function 页面滚动触发事件的处理函数
其他 Any 可以添加任意的函数或数据,在Page实例的其他函数中用 this 可以访问
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的生命周期是由微信客户端根据用户操作主动触发的。
页面的打开参数query,我们实现一个购物商城的小程序,我们需要完成一个商品列表页和商品详情页,点击商品列表页的商品就可以跳转到该商品的详情页,我们不可能为每个商品单独去实现它的详情页。我们只需要实现一个商品详情页的pages/detail/detail.(代表WXML/WXSS/JS/JSON文件)即可,在列表页打开商品详情页时把商品的id传递过来,详情页通过刚刚说的onLoad回调的参数option就可以拿到商品id,从而绘制出对应的商品。

// pages/list/list.js
// 列表页使用navigateTo跳转到详情页
wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })

// pages/detail/detail.js
Page({
  onLoad: function(option) {
        console.log(option.id)
        console.log(option.other)
  }
})

小程序把页面的打开路径定义成页面URL,其组成格式和网页的URL类似,在页面路径后使用英文 ? 分隔path和query部分,query部分的多个参数使用 & 进行分隔,参数的名字和值使用 key=value 的形式声明。在页面Page构造器里onLoad的option可以拿到当前页面的打开参数,其类型是一个Object,其键值对与页面URL上query键值对一一对应。

4. 页面的数据

宿主环境所提供的Page实例的原型中有setData函数,我们可以在Page实例下的方法调用this.setData把数据传递给渲染层,从而更新界面。由于渲染层和逻辑层分别在两个线程中运行,所以setData传递数据是异步的,所以setData的第二个参数是一个callback回调,在这次setData对界面渲染完毕后触发。
使用setData更新渲染层数据:

// page.js
Page({
  onLoad: function(){
    this.setData({//setData(data, callback)
      text: 'change data'//data是由多个key: value构成的Object对象
    }, function(){
      // 在这次setData对界面渲染完毕后触发
    })
  }
})

页面的data数据会涉及相当多的字段,你并不需要每次都将整个data字段重新设置一遍,你只需要把改变的值进行设置即可,宿主环境会自动把新改动的字段合并到渲染层对应的字段中。
使用setData更新渲染层数据:

// page.js
Page({
  data: {//data中的key还可以以数据路径的形式给出,例如 this.setData({"d[0]": 100}); this.setData({"d[1].text": 'Goodbye'});
    a: 1, b: 2, c: 3,
    d: [1, {text: 'Hello'}, 3, 4]
  }
  onLoad: function(){
       // a需要变化时,只需要setData设置a字段即可
    this.setData({a : 2})
  }
})

每次只设置需要改变的最小单位数据,可以提高小程序的渲染性能。
注意:

  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
    同上。在触发距离内滑动期间,本事件只会被触发一次。
  3. 页面滚动 onPageScroll
    监听用户滑动页面事件,参数为 Object,包含 scrollTop 字段,表示页面在垂直方向已滚动的距离(单位px)
  4. 用户转发 onShareAppMessage
    只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮,在用户点击转发按钮的时候会调用,此事件需要return一个Object,包含title和path两个字段,用于自定义转发内容
    使用onShareAppMessage自定义转发字:
// page.js
Page({
onShareAppMessage: function () {
 return {
   title: '自定义转发标题',
   path: '/page/user?id=123'
 }
}
})
6. 页面跳转和路由

一个小程序拥有多个页面,我们可以通过wx.navigateTo推入一个新的页面,如下图:
在这里插入图片描述
在首页使用2次wx.navigateTo后,页面层级会有三层,我们把这样的一个页面层级称为页面栈。
描述页面栈:[ pageA, pageB, pageC ],其中pageA在最底下,pageC在最顶上。页面栈最大层级为10层。
使用 wx.navigateBack() 可以退出当前页面栈的最顶上页面。
使用wx.redirectTo({ url: ‘pageE’ }) 是替换当前页变成pageE。当页面栈到达10层无法新增时,使用redirectTo这个API进行页面跳转。
小程序提供了原生的Tabbar支持,我们可以在app.json声明tabBar字段来定义Tabbar页
app.json定义小程序底部tab(页面最下方的导航页):

{
  "tabBar": {
    "list": [
      { "text": "Tab1", "pagePath": "pageA" },
      { "text": "Tab1", "pagePath": "pageF" },
      { "text": "Tab1", "pagePath": "pageG" }
    ]
  }
}

使用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 ]
页面路由触发方式及页面生命周期函数的对应关系:

路由方式 触发时机 路由前页面生命周期 路由后页面生命周期
初始化 小程序打开的第一个页面 onLoad, onShow
打开新页面 调用 API wx.navigateTo onHide onLoad, onShow
页面重定向 调用 API wx.redirectTo onUnload onLoad, onShow
页面返回 调用 API wx.navigateBack onUnload onShow
Tab 切换 调用 API wx.switchTab 参考文档 参考文档
重启动 调用 API wx.reLaunch onUnload onLoad, onShow

3.3 组件

一个小程序页面可以分解成多个部分,组件是小程序页面的基本组成单元。小程序的宿主环境提供了一系列基础组件,从而实现快速开发
组件是在WXML模板文件声明中使用的,使用标签名来引用一个组件,通常包含开始标签和结束标签,该标签的属性用来描述该组件:

<!-- page.wxml -->//所有组件名和属性都是小写,多个单词会以英文横杠 "-" 进行连接。
<image mode="scaleToFill" src="img.png"></image>

容器组件嵌套其他组件:

<view>
  <image mode="scaleToFill" src="img.png"></image>
  <view>//对于一些容器组件,其内容可以声明在其开始标签和结束标签之间。
    <view>1</view>
    <view>2</view>
    <view>3</view>
  </view>
</view>

所有组件都拥有下表列举的属性,主要涉及样式和事件绑定

属性名 类型 描述 其他说明
id String 组件的唯一标示 保持整个页面唯一
class String 组件的样式类 在对应的WXSS中定义的样式类
style String 组件的内联样式 可以通过数据绑定进行动态设置的内联样式
hidden Boolean 组件是否显示 所有组件默认显示
data-* Any 自定义属性 组件上触发的事件时,会发送给事件处理函数
bind / catch EventHandler 事件 详情见3.5节
组件都拥有各自自定义的属性,可以对该组件的功能或者样式进行修饰

3.4 API

用来调起微信提供的能力。所有小程序的API都挂载在wx对象底下。
通过wx.request发起网络请求:

wx.request({
url: 'test.php',
data: {},
header: { 'content-type': 'application/json' },
success: function(res) {
 // 收到https服务成功后返回
 console.log(res.data)
},
fail: function() {
 // 发生网络错误等情况触发
},
complete: function() {
 // 成功或者失败后触发
}
})

API调用大多都是异步的。有部分API会拉起微信的原生界面,此时会触发Page的onHide方法,当用户从原生界面返回到小程序时,会触发Page的onShow方法。
只要了解一般调用API的技巧,再通过官方API文档了解到对应的API参数细节即可。

3.5 事件

3.5.1 什么是事件

“用户在渲染层的行为反馈”(用户点击按钮)以及“组件的部分状态反馈”(播放进度变化)抽象为渲染层传递给逻辑层的“事件”。
在这里插入图片描述
事件处理示例:

<!-- page.wxml -->
<view id="tapTest" data-hi="WeChat" bindtap="tapName"> Click me! </view>

// page.js
   Page({
  tapName: function(event) {
    console.log(event)
  }
})

事件通过bindtap这个属性绑定在组件上,同时在当前页面的Page构造器中定义对应的事件处理函数tapName,当用户点击该view区域时,达到触发条件生成事件tap,该事件处理函数tapName被执行,同时收到一个事件对象event。

3.5.2 事件类型和事件对象

组件的事件可以参考其参数说明,见文档。
当事件回调触发的时候,会收到一个事件对象。
事件对象示例:

<!-- page.wxml -->
<view id="outer" catchtap="handleTap">
  <view id="inner">点击我</view>
</view>

// page.js
Page({
  handleTap: function(evt) {
       // 当点击inner节点时
    // evt.target 是inner view组件
       // evt.currentTarget 是绑定了handleTap的outer view组件
       // evt.type == “tap”
       // evt.timeStamp == 1542
       // evt.detail == {x: 270, y: 63}
       // evt.touches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
       // evt.changedTouches == [{identifier: 0, pageX: 270, pageY: 63, clientX: 270, clientY: 63}]
  }
})

3.5.3 事件绑定与冒泡捕获

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

  1. key以bind或者catch开头,然后跟上事件的类型,如bindtap、catchtouchstart。也可以写为bind:tap、catch:touchstart。同时bind和catch前还可以加上capture-来表示捕获阶段。
  2. value是一个字符串,需要在对应的页面Page构造器中定义同名的函数,bind和capture-bind的含义分别代表事件的冒泡阶段和捕获阶段,其触发的顺序如图
    在这里插入图片描述
    事件的冒泡和捕获:
<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>

点击 inner view 会先后调用handleTap2、handleTap4、handleTap3、handleTap1。
bind事件绑定不会阻止冒泡事件向上冒泡,catch事件绑定可以阻止冒泡事件向上冒泡。如果将上面代码中的第一个capture-bind改为capture-catch,将只触发handleTap2(capture-catch将中断捕获阶段和取消冒泡阶段)
自定义事件,如无特殊声明都是非冒泡事件

3.6 兼容

我们可能需要针对不同手机进行程序上的兼容,此时可以使用 wx.getSystemInfo 或者 wx.getSystemInfoSync 来获取手机品牌、操作系统版本号、微信版本号以及小程序基础库版本号等
新版本的宿主环境会提供一些新的API,你可以通过判断此API是否存在来做程序上的兼容。
小程序还提供了wx.canIUse这个API,用于判断接口或者组件在当前宿主环境是否可用,其参数格式为:

${API}.${method}.${param}.${options}或者${component}.${attribute}.${option}
  • ${API} 代表 API 名字
  • ${method} 代表调用方式,有效值为return, success, object,
    callback
    ${param} 代表参数或者返回值
    ${options} 代表参数的可选值
    ${component} 代表组件名字
    ${attribute} 代表组件属性
    ${option} 代表组件属性的可选值
发布了112 篇原创文章 · 获赞 56 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览