查看更多学习笔记:GitHub:LoveEmiliaForever
微信小程序开发指南
微信小程序开发文档
渲染层和逻辑层
WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层
- 渲染层和数据相关。
- 逻辑层负责产生、处理数据。
- 逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
通信模型
数据驱动
如果有某种“方法”可以让状态和视图绑定在一起(状态变更时,视图也能自动变更),那我们就可以省去手动修改视图的工作,这个方法就是数据驱动
WXML结构实际上等价于一棵Dom树,通过一个JS对象也可以来表达Dom树的结构
WXML可以先转成JS对象,然后再渲染出真正的Dom树
通过setData把msg数据从“Hello World”变成“Goodbye”,产生的JS对象对应的节点就会发生变化
此时可以对比前后两个JS对象得到变化的部分,然后把这个差异应用到原来的Dom树上
从而达到更新UI的目的,这就是数据驱动的原理
双线程下的界面渲染
在渲染层,宿主环境会把WXML转化成对应的JS对象
逻辑层发生数据变更时,通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层
经过对比前后差异,把差异应用在原来的Dom树上
程序与页面
从逻辑组成来说,一个小程序
是由多个页面
组成的程序
了解程序和页面的概念以及它们的生命周期是非常重要的
程序
小程序
指的是产品层面的程序,而程序
指的是代码层面的程序实例
为了避免误解,下文采用App
来代替代码层面的程序
概念
程序构造器App()
App()
构造器必须写在项目根目录的app.js
里,App实例是单例对象
,在其他JS脚本中可以使用宿主环境提供的 getApp()
来获取程序实例
App构造器接受一个Object
参数
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
程序的生命周期和打开场景
初次进入小程序的时候,微信客户端初始化好宿主环境,同时从网络下载或者从本地缓存中拿到小程序的代码包,把它注入到宿主环境,初始化完毕后,微信客户端就会给App实例派发onLaunch事件,App构造器参数所定义的onLaunch方法会被调用
可以看到,App的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该从其他代码里主动调用App实例的生命周期函数
在微信客户端中打开小程序有很多途径,微信客户端会把打开方式带给onLaunch
和onShow
的调用参数options
点击查看所有的途径参数(场景值)
onLaunch,onShow参数
支持返回 referrerInfo.appId
的场景
小程序全局数据
JS脚本是运行在JsCore的线程里,小程序的每个页面各自有一个WebView线程进行渲染
小程序切换页面时,小程序逻辑层的JS脚本运行上下文依旧在同一个JsCore线程中
App实例是单例的,因此不同页面直接可以通过App实例下的属性来共享数据
要特别留意一点,所有页面的脚本逻辑都跑在同一个JsCore线程,页面使用setTimeout或者setInterval的定时器,然后跳转到其他页面时,这些定时器并没有被清除,需要开发者自己在页面离开的时候进行清理
页面
一个小程序可以有很多页面,每个页面承载不同的功能,页面之间可以互相跳转
文件构成和路径
一个页面是分三部分组成:界面、配置和逻辑
一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的
页面路径需要在小程序代码根目录app.json
中的pages
字段声明,否则这个页面不会被注册到宿主环境中,默认pages字段的第一个页面路径为小程序的首页
页面构造器Page()
宿主环境提供了 Page()
构造器用来注册一个小程序页面,Page()在页面脚本page.js
中调用
Page构造器接受一个Object参数
页面的生命周期和打开参数
- 页面初次加载的时候,微信客户端就会给Page实例派发
onLoad
事件,onLoad在页面没被销毁之前只会触发1次 - 页面显示之后,Page构造器参数所定义的
onShow
方法会被调用,从别的页面返回到当前页面时,当前页的onShow方法都会被调用 - 页面初次渲染完成时,Page构造器参数所定义的
onReady
方法会被调用,onReady在页面没被销毁前只会触发1次 - 页面不可见时,Page构造器参数所定义的
onHide
方法会被调用,这种情况会在使用wx.navigateTo切换到其他页面、底部tab切换时触发 - 当前页面使用
wx.redirectTo
或wx.navigateBack
返回到其他页时,当前页面会被微信客户端销毁回收,此时Page构造器参数所定义的onUnload
方法会被调用
onLoad早于 onShow,onShow早于onReady
Page的生命周期是由微信客户端根据用户操作主动触发的。为了避免程序上的混乱,我们不应该在其他代码中主动调用Page实例的生命周期函数
页面的打开参数query
页面Page构造器里onLoad的option可以拿到当前页面的打开参数,其类型是一个Object,其键值对与页面URL上query键值对一一对应
微信小程序的页面链接和URL一样,同时也要用UrlEncode
页面的数据
data参数是页面第一次渲染时从逻辑层传递到渲染层的数据
Page实例的原型中有setData
函数,我们可以在Page实例下的方法调用this.setData
把数据传递给渲染层,从而达到更新界面的目的
setData传递数据实际是一个异步
的过程,所以setData的第二个参数是一个callback回调,在这次setData对界面渲染完毕后触发
data中的key还可以非常灵活,以数据路径的形式给出this.setData({"d[1].text": 'Goodbye'})
,注意此时key要用双引号
- 直接修改 Page实例的this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致
- 由于setData是需要两个线程的一些通信消耗,为了提高性能,每次设置的数据不应超过1024kB
- 不要把data中的任意一项的value设为undefined,否则可能会有引起一些不可预料的bug
页面的用户行为
- 下拉刷新
onPullDownRefresh
- 监听用户下拉刷新事件,需要在
app.json的window
选项中或页面配置page.json
中设置enablePullDownRefresh
为true - 当处理完数据刷新后,
wx.stopPullDownRefresh
可以停止当前页面的下拉刷新
- 监听用户下拉刷新事件,需要在
- 上拉触底
onReachBottom
- 监听用户上拉触底事件,可以在
app.json的window
选项中或页面配置page.json
中设置触发距离onReachBottomDistance
- 在触发距离内滑动期间,本事件只会被触发一次
- 监听用户上拉触底事件,可以在
- 页面滚动
onPageScroll
- 监听用户滑动页面事件,参数为 Object,包含
scrollTop
字段,表示页面在垂直方向已滚动的距离(单位px)
- 监听用户滑动页面事件,参数为 Object,包含
- 用户转发
onShareAppMessage
- 只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮
- 此事件需要return一个
Object,包含title和path
两个字段,用于自定义转发内容
页面跳转和路由
一个小程序拥有多个页面,我们可以通过wx.navigateTo
推入一个新的页面
页面以栈的形式表示,称为页面栈
,以列表形式书写(前底后顶)
小程序宿主环境限制了这个页面栈的最大层级为10层
wx.navigateTo({ url: 'pageD' })
可以往当前页面栈多推入(进入)一个 pageDwx.navigateBack()
可以推出(退出)当前页面栈的最顶上页面wx.redirectTo({ url: 'pageE' })
是替换当前页变成pageE
小程序提供了原生的Tabbar
支持,我们可以在app.json声明tabBar字段
来定义Tabbar页
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
- 使用
wx.switchTab({ url: 'pageF' })
,此时原来的页面栈会被清空(除了已经声明为Tabbar页外的其他页面会被销毁),然后会切到pageF所在的tab页面 wx.navigateTo
和wx.redirectTo
只能打开非TabBar页面,wx.switchTab
只能打开Tabbar页面- 使用
wx. reLaunch({ url: 'pageH' })
重启小程序,并且打开pageH
A和B是Tab页面,C由A进,D由C进:[A[C[D]], B]
Tabbar页面初始化之后不会被销毁
可以看到,这些页面的运作就是和栈一样的进入退出
组件和API
组件
一个小程序页面可以分解成多个部分组成,组件就是小程序页面的基本组成单元
小程序的宿主环境提供了一系列基础组件
用起来和Vue的组件、HTML标签差不多
全部组件的文档
API
宿主环境提供了丰富的API,可以很方便调起微信提供的能力
wx对象
实际上就是小程序的宿主环境所提供的全局对象,几乎所有小程序的API都挂载在wx对象底下
- wx.on* 开头的 API 是监听某个事件发生的API接口,接受一个 Callback 函数作为参数
- 如未特殊约定,多数 API 接口为
异步
接口 ,都接受一个Object作为参数 - API的Object参数一般由
success
、fail
、complete
三个回调来接收接口调用结果 - wx.get* 开头的API是获取宿主环境数据的接口
- wx.set* 开头的API是写入数据到宿主环境的接口
wx.request({
url: 'test.php',
data: {},
header: { 'content-type': 'application/json' },
success: function(res) {
// 接口调用成功触发
console.log(res.data)
},
fail: function() {
// 接口调用失败触发
},
complete: function() {
// 接口调用结束触发
// 成功或者失败后触发
}
})
有部分API会拉起微信的原生界面,此时会触发Page的onHide方法,当用户从原生界面返回到小程序时,会触发Page的onShow方法
API文档
事件
把这种用户在渲染层的行为反馈
以及组件的部分状态反馈
抽象为渲染层传递给逻辑层的事件
<!-- 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
事件类型和事件对象
常见事件类型
事件对象event
currentTarget为当前事件所绑定的组件,而target则是触发该事件的源头组件
target和currentTarget的event对象属性
touch和changedTouches的event对象属性
事件绑定与冒泡捕获
bind
或者catch
开头,然后跟上事件的类型,如bindtap
、catchtouchstart
,也可以是如bind:tap
、catch:touchstart
,bind和catch前还可以加上capture-
来表示捕获阶段
然后加上="FuncName"
,FuncName是对应的页面Page构造器中定义同名的函数
bind和capture-bind的含义分别代表事件的冒泡阶段和捕获阶段
bind事件
绑定不会阻止冒泡事件向上冒泡,catch事件
绑定可以阻止冒泡事件向上冒泡
兼容
小程序的宿主环境一直在迭代更新,为了让你的小程序在不同环境下都能提供相应的服务,我们需要来了解一下在小程序中如何实现兼容办法
我们可能需要针对不同手机进行程序上的兼容,此时可以使用 wx.getSystemInfo
或者 wx.getSystemInfoSync
来获取手机品牌、操作系统版本号、微信版本号以及小程序基础库版本号等
wx.getSystemInfoSync()
{
brand: "iPhone", // 手机品牌
model: "iPhone 6", // 手机型号
platform: "ios", // 客户端平台
system: "iOS 9.3.4", // 操作系统版本
version: "6.5.23", // 微信版本号
SDKVersion: "1.7.0", // 小程序基础库版本
language: "zh_CN", // 微信设置的语言
pixelRatio: 2, // 设备像素比
screenWidth: 667, // 屏幕宽度
screenHeight: 375, // 屏幕高度
windowWidth: 667, // 可使用窗口宽度
windowHeight: 375, // 可使用窗口高度
fontSizeSetting: 16 // 用户字体大小设置
}
随着宿主环境的更新,新版本的宿主环境会提供一些新的API,你可以通过判断此API是否存在来做程序上的兼容
if (wx.openBluetoothAdapter) {
wx.openBluetoothAdapter()
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
})
}
小程序还提供了wx.canIUse
这个API,用于判断接口或者组件在当前宿主环境是否可用
其参数格式为: ${API}.${method}.${param}.${options}
或者${component}.${attribute}.${option}
${API}
代表 API 名字${method}
代表调用方式,有效值为return, success, object, callback${param}
代表参数或者返回值${options}
代表参数的可选值${component}
代表组件名字${attribute}
代表组件属性${option}
代表组件属性的可选值
在不得已的情况下(小程序强依赖某个新的API或者组件时),还可以通过在小程序管理后台设置基础库最低版本设置
来达到不向前兼容的目的