微信小程序开发自学笔记 —— 三、理解小程序宿主环境

本文详细介绍了小程序的运行环境,包括渲染层和逻辑层的分离,数据如何通过setData在两层间同步,以及通信模型。同时,讨论了小程序的生命周期,如onLaunch、onShow、onHide等回调函数。此外,文章还涵盖了页面构造、页面数据管理、用户行为回调、页面跳转和路由,以及组件和API的使用。最后,提到了事件处理和不同环境的兼容性策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

理解小程序宿主环境

小程序可以调用宿主环境提供的微信客户端的能力。

渲染层和逻辑层

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

小程序如何把脚本里的数据渲染在界面上?

在WXML模板中,用{{}}语法绑定一个msg变量

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

在 JS 脚本使用 this.setData 方法把 msg 字段设置为“hello world”

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

小程序的渲染层和逻辑层分别有2个线程管理

  • 渲染层的界面使用了WebView进行渲染
  • 逻辑层采用JSCore线程运行脚本

这两个线程的通信会经由微信客户端做中转,逻辑层发送网络请求也经由微信客户端转发。

数据驱动

小程序的数据驱动基本原理

  • WXML结构实际上等价于一个DOM树,通过一个JS对象也可以来表达DOM树的结构
  • WXML可以先转为JS对象,然后再渲染出真正的DOM树
  • 当JS对象对应的节点通过setData产生变化,此时将前后两个JS对象得到变化的部分进行对比,然后将这个差异应用到原来的DOM上,从而达到更新UI的目的。
双线程下的界面渲染

小程序的逻辑层和渲染层是分开的两个线程。

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

程序与界面

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

程序

小程序是指产品层面的程序,而“程序”指的是代码层面的程序实例。

程序构造器 App()

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

App构造器接受一个Object参数,如onLaunch / onShow / onHide 三个回调时App实例的生命周期函数。

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

App构造器的参数

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

初次进入小程序的时候,微信客户端初始化好宿主环境,同时从网络下载或者从本地缓存中拿到小程序的代码包,把它注入到宿主环境,初始化完毕后,微信客户端就会给App实例派发onLaunch事件,App构造器参数所定义的onLaunch方法会被调用。
进入小程序之后,用户可以点击右上角的关闭,或者按手机设备的Home键离开小程序,此时小程序并没有被直接销毁,我们把这种情况称为“小程序进入后台状态”,App构造器参数所定义的onHide方法会被调用。
当再次回到微信或者再次打开小程序时,微信客户端会把“后台”的小程序唤醒,我们把这种情况称为“小程序进入前台状态”,App构造器参数所定义的onShow方法会被调用。

onLaunch,onShow参数

字段类型描述
pathString打开小程序的页面路径
queryObject打开小程序的页面参数query
sceneNumber打开小程序的场景值,详细场景值请参考小程序官方文档
shareTicketStringshareTicket,详见小程序官方文档
referrerInfoObject当场景为由从另一个小程序或公众号或App打开时,返回此字段
referrerInfo.appIdString来源小程序或公众号或App的 appId,详见下方说明
referrerInfo.extraDataObject来源小程序传过来的数据,scene=1037或1038时支持

以下场景支持返回 referrerInfo.appId

场景值场景appId信息含义
1020公众号 profile页相关小程序列表 返回来源公众号 appId
1035公众号自定义菜单返回来源公众号 appId
1036App 分享消息卡片返回来源应用 appId
1037小程序打开小程序返回来源小程序 appId
1038从另一个小程序返回返回来源小程序 appId
1043公众号模板消息返回来源公众号 appId
小程序全局数据

App构造器可以传递其他参数作为全局属性以达到全局共享数据的目的。

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

页面

一个小程序可以有很多页面,每个页面承载不同的功能,页面之间可以互相跳转。

文件构成和路径

一个页面分三部分组成

  • 界面 —— 由WXML文件和WXSS文件来负责描述
  • 配置 —— 由JSON文件进行描述
  • 逻辑 —— 由JS脚本文件负责

页面路径需要在小程序代码根目录app.json中的pages字段声明,否则不会被注册到宿主环境中。

{
  "pages":[
    "pages/index/page", // 第一项默认为首页
    "pages/other/other"
  ]
}
页面构造器Page()

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

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构造器的参数

参数属性类型描述
dataObject页面的初始数据
onLoadFunction生命周期函数–监听页面加载,触发时机早于onShow和onReady
onReadyFunction生命周期函数–监听页面初次渲染完成
onShowFunction生命周期函数–监听页面显示,触发事件早于onReady
onHideFunction生命周期函数–监听页面隐藏
onUnloadFunction生命周期函数–监听页面卸载
onPullDownRefreshFunction页面相关事件处理函数–监听用户下拉动作
onReachBottomFunction页面上拉触底事件的处理函数
onShareAppMessageFunction用户点击右上角转发
onPageScrollFunction页面滚动触发事件的处理函数
其他Any可以添加任意的函数或数据,在Page实例的其他函数中用 this 可以访问
页面的生命周期和打开参数
  • 页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器参数所定义的onLoad方法会被调用,onLoad在页面没被销毁之前只会触发1次,在onLoad的回调中,可以获取当前页面所调用的打开参数option,
  • 页面显示之后,Page构造器参数所定义的onShow方法会被调用,一般从别的页面返回到当前页面时,当前页的onShow方法都会被调用。
  • 在页面初次渲染完成时,Page构造器参数所定义的onReady方法会被调用,onReady在页面没被销毁前只会触发1次,onReady触发时,表示页面已经准备妥当,在逻辑层就可以和视图层进行交互了。
  • 页面不可见时,Page构造器参数所定义的onHide方法会被调用,这种情况会在使用wx.navigateTo切换到其他页面、底部tab切换时触发。
    当前页面使用wx.redirectTo或wx.navigateBack返回到其他页时,当前页面会被微信客户端销毁回收,此时Page构造器参数所定义的onUnload方法会被调用。
页面的数据

小程序的页面结构由WXML进行描述,WXML可以通过数据绑定的语法绑定从逻辑层传递过来的数据字段。数据来自于页面Page构造器的data字段,data参数是页面第一次渲染时从逻辑层传递到渲染层的数据。

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

setData其一般调用格式是 setData(data, callback),其中data是由多个key: value构成的Object对象。

// page.js
Page({
  onLoad: function(){
    this.setData({
      text: 'change data'
    }, function(){
      // 在这次setData对界面渲染完毕后触发
    })
  }
})

此外需要注意以下3点:

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

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

  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所示。
// page.js
Page({
onShareAppMessage: function () {
 return {
   title: '自定义转发标题',
   path: '/page/user?id=123'
 }
}
})
页面跳转和路由

页面路由触发方式及页面生命周期函数的对应关系

路由方式触发时机路由前页面生命周期路由后页面生命周期
初始化小程序打开的第一个页面onLoad, onShow
打开新页面 调用API wx.navigateToonHideonLoad, onShow
页面重定向 调用API wx.redirectToonUnloadonLoad, onShow
页面返回 调用API wx.navigateBackonUnloadonShow
Tab切换 调用 API wx.switchTab请参考表3-6请参考表3-6
重启动调用 API wx.reLaunchonUnloadonLoad, onShow

页面路由触发方式及页面生命周期函数的对应关系

当前页面路由后页面触发的生命周期(按顺序)
AA
ABA.onHide(), B.onLoad(), B.onShow()
AB(再次打开)A.onHide(), B.onShow()
CAC.onUnload(), A.onShow()
CBC.onUnload(), B.onLoad(), B.onShow()
DBD.onUnload(), C.onUnload(), B.onLoad(), B.onShow()
D(从转发进入)AD.onUnload(), A.onLoad(), A.onShow()
D(从转发进入)BD.onUnload(), B.onLoad(), B.onShow()

组件

一个小程序页面可以分解成多个部分,组件就是小程序页面的基本组成单元。小程序的宿主环境提供了一系列基础组件。

组件是在WXML模板文件声明中使用的,小程序使用标签名来引用一个组件通常包含开始标签和结束标签,改标签的属性用来描述该组件。

组件共有属性

属性名类型描述其他说明
idString组件的唯一标示保持整个页面唯一
classString组件的样式类在对应的WXSS中定义的样式类
styleString组件的内联样式可以通过数据绑定进行动态设置的内联样式
hiddenBoolean组件是否显示所有组件默认显示
data-*Any自定义属性组件上触发的事件时,会发送给事件处理函数
bind / catchEventHandler事件详情见3.5节

组件都拥有各自自定义的属性,可以对该组件的功能或者样式进行修饰。

Image图片组件属性

属性名类型默认值描述
srcString图片资源地址
modeString‘scaleToFill’图片裁剪、缩放的模式
lazy-loadBooleanfalse图片懒加载。只针对page与scroll-view下的image有效 1.5.0
binderrorHandleEvent当错误发生时触发事件,事件对象event.detail = {errMsg: ‘something wrong’}
bindloadHandleEvent当图片载入完毕时触发事件,事件对象event.detail = {height:‘图片高度px’, width:‘图片宽度px’}

API

宿主环境提供了丰富的API,可以很方便调起微信提供的能力。其中wx对象就是小程序宿主环境所提供的全局对象,几乎所有小程序的API都挂载在wx对象底下。(除了Page/App等特殊的构造器)。

小程序提供的API按照功能主要分为几大类:网络、媒体、文件、数据缓存、位置、设备、界面、界面节点信息还有一些特殊的开放接口。

API一般调用的约定:

  1. wx.on* 开头的 API 是监听某个事件发生的API接口,接收一个 Callback 函数作为参数。当该事件触发时,会调用 Callback函数。
  2. 如未特殊约定,多数 API 接口为异步接口,都接收一个Object作为参数。
  3. API 的 Object 参数一般由success、fail、complete三个回调来接收接口调动结果。
  4. wx.get* 开头的API是获取宿主环境数据的接口。
  5. wx.set* 开头的API是写入数据到宿主环境的接口。
wx.request({
    url:'test.php',
    data:{},
    header:{'content-type':'application/json'},
    success:function(res){
        // 收到https服务成功后返回
        console.log(res.data)
    },
    fail:function(){
        // 发生网络错误等情况触发
    },
    complete:function(){
        // 成功或者失败后触发
    }
})

API接口回调说明

参数名字类型必填描述
successFunction接口调用成功的回调函数
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

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

事件

什么是事件

在小程序中,“用户在渲染层的行为反馈” 以及 “组件的部分状态反馈” 被抽象为渲染层传递给逻辑层的“事件”。

<!-- 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。

事件类型和事件对象

常见的事件类型

类型触发条件
touchstart手指触摸动作开始
touchmove手指触摸后移动
touchcancel手指触摸动作被打断,如来电提醒,弹窗
touchend手指触摸动作结束
tap手指触摸后马上离开
longpress手指触摸后,超过350ms再离开,如果指定了事件回调函数并触发了这个事件,tap事件将不被触发
longtap手指触摸后,超过350ms再离开(推荐使用longpress事件代替)
transitionend会在 WXSS transition 或 wx.createAnimation 动画结束后触发
animationstart会在一个 WXSS animation 动画开始时触发
animationiteration会在一个 WXSS animation 一次迭代结束时触发
animationend会在一个 WXSS animation 动画完成时触发

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

事件对象属性

属性类型说明
typeString事件类型
timeStampInteger页面打开到触发事件所经过的毫秒数
targetObject触发事件的组件的一些属性值集合
currentTargetObject当前组件的一些属性值集合
detailObject额外的信息
touchesArray触摸事件,当前停留在屏幕中的触摸点信息的数组
changedTouchesArray触摸事件,当前变化的触摸点信息的数组

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

target和currentTarget事件对象属性

属性类型说明
idString当前组件的id
tagNameString当前组件的类型
datasetObject当前组件上由data-开头的自定义属性组成的集合

touch和changedTouches对象属性

属性类型说明
identifierNumber触摸点的标识符
pageX, pageYNumber距离文档左上角的距离,文档的左上角为原点 ,横向为X轴,纵向为Y轴
clientX, clientYNumber距离页面可显示区域(屏幕除去导航条)左上角距离,横向为X轴,纵向为Y轴
事件绑定与冒泡捕获

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

  • key以bind或者catch开头,然后跟上事件的类型
  • value是一个字符串,需要在对应的页面Page构造器中定义同名的函数,否则触发事件时控制台会报错。
  • bind和capture-bind分别代表事件的冒泡阶段和捕获阶段,capture-表示捕获阶段
兼容

小程序中可以使用wx.getSystemInfo 或者 wx.getSystemInfoSync 来获取手机品牌、微信版本号等信息。通过这个信息,我们可以针对不同平台做差异化的服务,实现小程序在不同环境下兼容。

随着宿主环境的更新,新版本的宿主环境会提供一些新的API,可以通过判断此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} 代表组件属性的可选值

wx.canIUse调用示例

// 判断接口及其参数在宿主环境是否可用
wx.canIUse('openBluetoothAdapter')
wx.canIUse('getSystemInfoSync.return.screenWidth')
wx.canIUse('getSystemInfo.success.screenWidth')
wx.canIUse('showToast.object.image')
wx.canIUse('onCompassChange.callback.direction')
wx.canIUse('request.object.method.GET')

 // 判断组件及其属性在宿主环境是否可用
wx.canIUse('contact-button')
wx.canIUse('text.selectable')
wx.canIUse('button.open-type.contact')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morgan_Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值