文章目录
前言
在上一章节中,带大家解读了 微信小程序 的项目基本结构,以及各项目配置文件的作用和用法,目前大家都知道小程序是属于 寄生应用
,而它的轻量化,及高效化的优势,让它一度成为主流的开发趋势。
但是,前台的便捷化,就意味着背后更多的事情,是由其他机制代完成了处理,那么作为微信小程序的 寄生宿主
,微信客户端基座生态环境
,为微信小程序 提供了哪些支持以及优化呢?这个问题,也就是本章节 我要给大家带来分享的内容,谈谈,微信小程序的背后,微信为其做了些什么?
生态框架
小程序开发框架的目标是通过尽可能简单、高效
的方式让开发者可以在微信中开发具有原生 APP 体验的服务。小程序可以调用很多宿主环境为其提供的微信客户端的能力,这就使得小程序比普通网页拥有更多的能力。
整个小程序框架系统主要围绕两部分展开:逻辑层(App Service)和 视图层(View)
。小程序提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript 的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑。
这也体现了,微信客户端在对小程序的生态适配支持上,已经形成了一套完善的框架思想体系。
这些框架生态的支持,主要体现在以下几个方面:
响应式数据绑定
微信小程序生态框架的核心是一个响应式的数据
绑定系统,可以让数据与视图非常简单地保持同步。当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
如下例:
<view> {{ title }}! </view>
<button bindtap="changeName"> Click me! </button>
Page({
data: {
title: "你好"
},
changeName() {
this.setData({
title: '微信小程序'
})
},
})
- 开发者通过框架将逻辑层数据中的 title 与视图层的 title 进行了绑定,所以在页面一打开的时候会显示 “你好”;
- 当点击按钮的时候,视图层会发送 changeName 的事件给逻辑层,逻辑层找到并执行对应的事件处理函数;
- 回调函数触发后,逻辑层执行
setData
的操作,将 data 中的 title 从 “你好 ”变为 “微信小程序”,因为该数据和视图层已经绑定了,从而视图层会自动改变为 “微信小程序”。
页面管理
生态框架 管理了整个小程序的页面路由,可以做到页面间的无缝切换,并给以页面完整的生命周期
。开发者需要做的只是将页面的数据、方法、生命周期函数注册到 生态框架 中,其他的一切复杂的操作都交由 生态框架 处理。
如图: 只需要在 app.json 中,配置好页面路径,框架则会自动接管页面的处理,以及路由视图的调配。
基础组件
一个小程序页面可以分解成多个部分组成,组件就是小程序页面的基本组成单元。
生态框架 提供了一套基础的组件,这些组件自带微信风格的样式以及特殊的逻辑,开发者可以通过组合基础组件,就可以构建出强大的 微信小程序 项目。
丰富的API 接口
生态框架 提供丰富的微信原生 API
,可以方便快捷的调用起 微信底层 提供的能力,如获取用户信息,本地存储,支付功能等。
视图层与逻辑层
逻辑层 (App Service)
小程序开发框架的 逻辑层 使用 JavaScript 引擎
为小程序提供开发 JavaScript
代码的运行环境以及微信小程序的特有功能。
逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。
开发者写的所有代码最终将会打包成一份 JavaScript 文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似 ServiceWorker,所以逻辑层也称之为 App Service。
在 JavaScript
的基础上,微信小程序框架增加了一些功能,以方便小程序的开发:
- 增加
App
和Page
方法,进行程序注册和页面注册
。 - 增加
getApp
和getCurrentPages
方法,分别用来获取 App 实例和当前页面栈。 - 提供丰富的 API,如微信用户数据,扫一扫,支付等微信特有能力。
- 提供模块化能力,每个页面有独立的作用域。
注意
:
小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript
在 web 中一些能力都无法使用,如 window
,document
等 。
注册小程序
每个小程序都需要在 app.js 中调用 App 方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。点击查看更多API配置
如下在app.js中
App({
onLaunch (options) {
// Do something initial when launch.
},
onShow (options) {
// Do something when show.
},
onHide () {
// Do something when hide.
},
onError (msg) {
console.log(msg)
},
globalData: 'I am global data'
})
整个小程序只有一个 App 实例,是全部页面共享的。开发者可以通过 getApp 方法获取到全局唯一的 App 实例
,获取App上的数据或调用开发者注册在 App 上的函数。
const appInstance = getApp()// 获取 app 实例。
console.log(appInstance.globalData) // I am global data
注意
:
- 不要在定义于 App() 内的函数中,或调用 App 前调用 getApp() ,使用 this 就可以拿到 app 实例。
- 通过 getApp() 获取实例之后,不要私自调用生命周期函数。
注册页面
对于小程序中的每个页面,都需要在页面对应的 js 文件中进行注册,指定页面的初始数据、生命周期回调、事件处理函数等。
使用 Page 构造器注册页面:查看 Page 构造器配置
还可以使用 Component (自定义组件) 构造器构造页面 :
Page 构造器适用于简单的页面。但对于复杂的页面, Page 构造器可能并不好用。
此时,可以使用 Component 构造器来构造页面。 Component 构造器的主要区别是:方法需要放在 methods: { } 里面。
页面路由
在小程序中所有页面的路由全部由生态框架进行管理
。
页面栈
框架以栈的形式维护了当前的所有页面。 当发生路由切换的时候,页面栈的表现如下:
路由方式 | 页面栈表现 |
---|---|
初始化 | 新页面入栈 |
打开新页面 | 新页面入栈 |
页面重定向 | 当前页面出栈,新页面入栈 |
页面返回 | 页面不断出栈,直到目标返回页 |
Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 |
重加载 | 页面全部出栈,只留下新的页面 |
开发者可以使用 getCurrentPages()
函数获取当前页面栈。数组中第一个元素为首页,最后一个元素为当前页面。
注意
:
- 不要尝试修改页面栈,会导致路由以及页面状态错误。
- 不要在 App.onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。
页面路由器对象。可以通过 this.pageRouter 或 this.router 获得当前页面或自定义组件的路由器对象。
路由的相对路径
页面路由器有 switchTab reLaunch redirectTo navigateTo navigateBack
五个方法,与 wx 对象向同名的五个方法 switchTab reLaunch redirectTo navigateTo navigateBack 功能相同;唯一的区别是,页面路由器中的方法调用时,相对路径永远相对于 this 指代的页面或自定义组件。
如下:
Page({
wxNavAction: function () {
wx.navigateTo({
url: './new-page'
})
},
routerNavAction: function () {
this.pageRouter.navigateTo({
url: './new-page'
})
}
})
switchTab | wx. switchTab |
reLaunch | wx.reLaunch |
redirectTo | wx.redirectTo |
navigateTo | wx.navigateTo |
navigateBack | wx.navigateBack |
注意事项
:
navigateTo, redirectTo 只能打开非 tabBar 页面。
switchTab 只能打开 tabBar 页面。
reLaunch 可以打开任意页面。
页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
调用页面路由带的参数可以在目标页面的onLoad中获取。
模块化
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。
注意
:
- exports 是 module.exports 的一个引用,因此在模块里边随意更改 exports 的指向会造成未知的错误。所以更推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰知道这两者的关系。
- 小程序目前不支持直接引入 node_modules , 开发者需要使用到 node_modules 时候建议拷贝出相关的代码到小程序的目录中,或者使用小程序支持的 npm 功能。
如:
// common.js
function sayHello(name) {
console.log(`Hello ${name} !`)
}
function sayGoodbye(name) {
console.log(`Goodbye ${name} !`)
}
module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye
在需要使用这些模块的文件中,使用 require 将公共代码引入
let common = require('common.js')
Page({
helloMINA: function() {
common.sayHello('MINA')
},
goodbyeMINA: function() {
common.sayGoodbye('MINA')
}
})
文件作用域
在 JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。
通过全局函数 getApp 可以获取全局的应用实例,如果需要全局的数据可以在 App() 中设置,
API 支持
小程序开发框架提供丰富的微信原生 API,可以方便的调起微信提供的能力,如获取用户信息,本地存储,支付功能等。
几乎所有小程序的API都挂载在wx对象底下(除了Page/App等特殊的构造器),所以讲述到API概念时,通常指的是wx对象底下的方法详细介绍请参考 API配置文档
微信小程序中,不同的 API 所实现的功能也不一样,所以,这里把API 按照同类型,可以大致分为以下几类:
事件监听API
以 on 开头的 API 用来监听某个事件是否触发,如:wx.onSocketOpen,wx.onCompassChange 等。
这类 API 接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。
同步API
以 Sync 结尾的 API 都是同步 API, 如 wx.setStorageSync,wx.getSystemInfoSync 等。此外,也有一些其他的同步 API,如 wx.createWorker,wx.getBackgroundAudioManager 等,
同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。
异步API
大多数 API 都是异步 API,如 wx.request,wx.login 等。这类 API 接口通常都接受一个 Object 类型的参数,这个参数都支持按需指定以下字段来接收接口调用结果:
回调参数
异步API 返回 Promise
基础库 2.10.2 版本起,异步 API 支持 callback & promise 两种调用方式。当接口参数 Object 对象中不包含 success/fail/complete 时将默认返回 promise,否则仍按回调方式执行,无返回值。
云开发API
开通并使用微信云开发
,即可使用云开发API,在小程序端直接调用服务端的云函数。
通信模型
小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore 线程运行JS脚本。一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下图中采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发,小程序的通信模型如图:
数据驱动
在开发UI界面过程中,程序需要维护很多变量状态,同时要操作对应的UI元素。随着界面越来越复杂,我们需要维护很多变量状态,同时要处理很多界面上的交互事件,整个程序变得越来越复杂。通常界面视图和变量状态是相关联的,如果有某种“方法”可以让状态和视图绑定在一起(视图变,数据变,数据变,视图变),那我们就可以省去手动修改视图的工作。
这个方法就是“数据驱动”,下边我们来介绍一下小程序的数据驱动基本原理。
WXML结构实际上等价于一棵Dom树,通过一个JS对象也可以来表达Dom树的结构,如图:
WXML可以先转成虚拟Dom,而虚拟Dom 本质上就是一个JS对象,然后再渲染出真正的Dom树,这和 Vue 的思想很像,我们可以看到转换的过程案例如图:
通过setData 把msg数据从“Hello World”变成“Goodbye”,产生的JS对象对应的节点就会发生变化,此时可以对比前后两个JS对象得到变化的部分(同 Vue diff 虚拟Dom 匹对算法),然后把这个差异应用到原来的Dom树上,从而达到更新UI的目的,这就是“数据驱动”的原理,如图:
双线程下的界面渲染
小程序的逻辑层和渲染层是分开的两个线程。在渲染层,宿主环境会把WXML转化成对应的JS对象,在逻辑层发生数据变更的时候,我们需要通过宿主环境提供的setData方法把数据从逻辑层传递到渲染层,再经过对比前后差异,把差异应用在原来的Dom树上,渲染出正确的UI界面,大致流程如下图:
视图层 (View)
小程序的运行环境分成视图层和逻辑层,在前面提到过 WXML 模板和 WXSS 样式就工作在视图层,JS 脚本则工作在逻辑层。小程序的视图层和逻辑层分离是经过很多考虑得出来的模型。
- 框架的视图层由 WXML 与 WXSS 编写,由组件来进行展示。
- 将逻辑层的数据反映成视图,同时将视图层的事件发送给逻辑层。
- WXML(WeiXin Markup language) 用于描述页面的结构。
- WXS(WeiXin Script) 是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。
- WXSS(WeiXin Style Sheet) 用于描述页面的样式。
- 组件(Component)是视图的基本组成单元。
WXS
注意事项
:
- WXS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。
- WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。
- WXS 的运行环境和其他 JavaScript 代码是隔离的,WXS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API。
- 由于运行环境的差异,在 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异。
查看更多WXS 语法配置
事件系统
什么是“事件系统”?
UI界面的程序需要和用户互动,例如用户可能会点击你界面上某个按钮,又或者长按某个区域,这类反馈应该通知给开发者的逻辑层,需要将对应的处理状态呈现给用户。
有些时候程序上的“行为反馈”不一定是用户主动触发的,例如我们在视频video播放的过程中,播放进度是会一直变化的,这种反馈也应该通知给开发者做相应的逻辑处理。
在小程序里边,我们把这种“用户在渲染层的行为反馈”以及“组件的部分状态反馈”抽象为渲染层传递给逻辑层的“事件行为过程”,如图:
事件描述还可以概述为以下几点:
- 事件是视图层到逻辑层的通讯方式。
- 事件可以将用户的行为反馈到逻辑层进行处理。
- 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
- 事件对象可以携带额外信息,如 id, dataset, touches。
事件分类
- 冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
- 非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。
兼容支持
小程序的宿主环境一直在迭代更新,提供更多的能力给开发者去完成更多的事情,所以你的小程序会运行在不同版本的宿主环境下。为了让你的小程序在不同环境下都能提供相应的服务,我们需要来了解一下在小程序中如何实现兼容办法。
我们可能需要针对不同手机进行程序上的兼容,此时可以使用 wx.getSystemInfo 或者 wx.getSystemInfoSync 来获取手机品牌、操作系统版本号、微信版本号以及小程序基础库版本号等,通过这个信息,我们可以针对不同平台做差异化的服务。
例如:通过wx.getSystemInfoSync获取宿主环境信息
wx.getSystemInfoAsync({
success (res) {
console.log(res.brand) //获取到设备品牌
}
})
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: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
})
}
我们可以选择合适的判断方法来做小程序的向前兼容,以保证我们的小程序在旧版本的微信客户端也能工作正常。在不得已的情况下(小程序强依赖某个新的API或者组件时),还可以通过在小程序管理后台设置“基础库最低版本设置”来达到不向前兼容的目的。例如你选择设置你的小程序只支持1.5.0版本以上的宿主环境,那么当运行着1.4.0版本宿主环境的微信用户打开你的小程序的时候,微信客户端会显示当前小程序不可用,并且提示用户应该去升级微信客户端。
总结
在本章节中,给大家分享介绍了 微信客户端 的宿主环境的基本运行机制以及它所提供的各种能力,组合这些能力可以完成一个体验非常流畅的小程序,同时也了解到如何对小程序在不同环境下兼容的办法,以便给不同环境下的的微信用户提供可靠或者降级的服务。
总览全局,就会发现,微信客户端 提供的生态框架支持,已相当完善,这将助力于我们开发者更加高效化的打造一款小程序。后续,我相信在这些方面会越来越生态体系化。
🚵♂️ 博主座右铭:向阳而生,我还在路上!
——————————————————————————————
🚴博主想说:将持续性为社区输出自己的资源,同时也见证自己的进步!
——————————————————————————————
🤼♂️ 如果都看到这了,博主希望留下你的足迹!【📂收藏!👍点赞!✍️评论!】
——————————————————————————————