原理架构
JSBridge
作为 native 与 JS 之间相互通信的桥梁
JS 部分(bridge): 在 JS 环境中注入 bridge 的实现代码,包含了协议的拼装 / 发送 / 参数池 / 回调池等一些基础功能。
Native 部分(SDK): 在客户端中 bridge 的功能映射代码,实现了 URL 拦截与解析 / 环境信息的注入 / 通用功能映射等功能。
逻辑层与视图层
1、逻辑层负责计算逻辑处理
2、视图层负责页面展示
数据变更驱动视图更新;视图交互触发事件,事件响应函数修改数据再次触发视图更新(用户交互,触发逻辑计算,最后由视图更新展示)
原理
vue – data -> 小程序
vue <- 事件响应 – 小程序
1、生命周期关联:在小程序上触发数据更新,由 vue 处理,vue 处理完后同步到小程序
为实现数据同步,mpvue 修改了 Vue.js runtime 实现,在 Vue.js 的生命周期中增加了更新小程序数据的逻辑。
2、事件代理机制:用户交互触发的数据更新通过事件代理机制完成,在小程序触发的事件代理到 vue 的 method 上
在小程序组件节点上触发事件后,找到虚拟 DOM 上对应的节点,触发对应的事件;另一方面,Vue.js 事件响应如果触发了数据更新,其生命周期函数更新将自动触发,在此函数上同步更新小程序数据,数据同步也就实现了
Vue 代码
1、将小程序页面编写为 Vue.js 实现
2、以 Vue.js 开发规范实现父子组件关联
小程序代码
1、以小程序开发规范编写视图层模板
2、配置生命周期函数,关联数据更新调用
3、将 Vue.js 数据映射为小程序数据模型
并在此基础上,附加如下机制
1、Vue.js 实例与小程序 Page 实例建立关联
2、小程序和 Vue.js 生命周期建立映射关系,能在小程序生命周期中触发 Vue.js 生命周期
3、小程序事件建立代理机制,在事件代理函数中触发与之对应的 Vue.js 组件事件响应
Vue.js 视图层渲染由 render 方法完成,同时在内存中维护着一份虚拟 DOM,mpvue 无需使用 Vue.js 完成视图层渲染,通过改造 render 方法,禁止视图层渲染;在 Vue runtime 时,添加平台 mpvue
事件代理机制
1、在 mpvue 生成的 wxml 文件中,所有的事件都被 handleProxy 的函数接管,在 handleProxy 进行处理后再去调用我们写的真正的事件处理函数。这个方法在 initMp 时,作为小程序 Page 构造函数的一个选项,从而可以在 wxml 中被正确调用
2、handleProxy 将小程序的 event 对象传给 handleProxyWithVue 函数进行进一步处理
3、handleProxyWithVue 的作用
- 从根实例开始,根据 comkey 找出事件处理函数所在的 mpvue 实例(getVm)
- 通过遍历找到的 vm 的 vnode,结合 eventid 找到小程序事件对应的真实的事件处理函数(getHandle)
- 将小程序的 event 对象包装为 mpvue 的 event 对象(getWebEventByMP), 并添加了 preventDefault 和 stopPropagation 方法
- 将包装后的 event 对象传给真实的处理函数进行调用。
(生成的 wxml 中绑定事件的节点都有 data-comkey 和 data-eventid 属性,在一个事件触发时,它们是被用来寻找事件对应的 vm 实例和对应的事件处理函数的)
生命周期
微信小程序:
1、App 对象,主要有 onLaunch, onShow 和 onHide
2、Page 对象,主要有 onLoad, onShow, onReady, onHide 和 onUnload
Vue 的生命周期主要体现在 8 个钩子:
beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed
vue & mpvue 生命周期
mpvue 生命周期图
vuw & mpvue 生命周期对比图
过程
app 启动:
app-beforeCreate -> app-created -> app-onlaunch -> app-beforeMount -> app-mounted -> app-onShow
进入页面,更新操作:
page beforeCreate -> page created -> page onLoad -> page onShow -> page onReady -> page beforeMount -> component beforeCreate -> component created -> component onLoad -> component onReady -> component beforeMount -> component mounted -> page mounted
返回(隐藏),没有触发 destroy:
page onUnload -> component onUnload
测试
page A、B(page tab)、C、D(page tab) (c 是 A 的子组件)
A -> B -> A(按钮跳转)
A -> B (点击左上角返回键)
1、App 启动
app-beforeCreate…..
app-created…
app-onlaunch…
app-beforeMount…..
app-mounted…..
app-onShow….
A-beforeCreate…..
A-created….
B-beforeCreate…..
B-created…..
D-beforeCreate…..
D-created…..
2、进入 page A
A-onLoad….
A-onShow….
A-onReady…..
A-beforeMount…..
C-beforeCreate…..
C-created…..
C-onLoad….
C-onReady…..
C-beforeMount…..
C-mounted…..
A-mounted…..
3、离开 page A
A-onUnload….
C-onUnload….
4、进入 page B
B-onLoad….
B-onShow….
B-onReady…..
B-beforeMount…..
B-mounted…..
B-beforeUpdate….. // 更新
B-updated…..
5、离开 page B
B-onHide…..
6、重新进入 page A(switchTab)
A-onLoad….
C-onLoad….
A-onShow….
C-onShow….
A-onReady…..
C-onReady…..
A-beforeMount…..
A-beforeUpdate…..
A-mounted…..
C-beforeUpdate…..
C-updated…..
7、通过返回键返回 page B
A-onUnload….
C-onUnload….
B-onShow….
创建项目
# 安装 vue-cli
$ npm install --global vue-cli
# 根据模板项目创建本地项目,目前为内网地址
$ vue init mpvue/mpvue-quickstart my-project
# 安装依赖和启动自动构建
$ cd my-project
$ npm install
# 现在支持微信 wx、头条 tt、支付宝 my、百度 swan,运行该命令生成 dist 文件夹,即小程序
$ npm run dev
目录结构
├── build 编译配置
├── config 编译配置
├── dist 小程序运行代码目录(该目录由编译生成)├── `node_modules`
├── src 开发目录
| ├── components 组件目录
| | ├── a.vue 组件 a
| | └── b.vue 组件 b
| ├── pages 页面目录
| | ├── index index 页面(默认会在 src/main.js 主入口生成 pages 配置,路径为 pages/index/main)├── index.vue 由其入口文件编译 main.js 后,生成 index/main.js、index/main.wxml 和 index/main.wxss 文件
├── main.js 页面默认入口文件(config 属性会编译为页面配置文件 index/main.json)| | └── other other 页面(默认会在 src/main.js 主入口生成 pages 配置,路径为 pages/other/main)├── index.vue 由其入口文件编译 main.js 后,生成 other/main.js、other/main.wxml 和 other/main.wxss 文件
├── main.js 页面默认入口文件(config 属性会编译为页面配置文件 other/main.json)| └── main.js 小程序配置项(全局数据、样式、声明钩子等;经 build 后,会在 dist 目录下生成 app.js、app.json 和 app.wxss 文件)└── static 静态文件,会直接被复制到 dist/ 下
└── package.json 项目的 package 配置
└── project.config.json 小程序开发工具的配置
项目 demo
自己写的 demo,在之前原生微信小程序的基础上,使用 mpvue 重构了电影模块:
mpvue-play
注意
以下是一些自己在写 demo 时,踩过的坑或是一些需要注意的点(尤其对习惯了 vue 的童学),具体也可参见 mpvue 文档
1、onShow 代替 mounted:从一个页面返回到前一页面时,mounted 不会再次被调用,因为页面没有被销毁,因而不需要重新挂载
onLoad 代替 created:启动项目时,所有页面的 beforeCreatecreated 都已调用,并且进入页面时,不会再调用 beforeCreatecreated
虽然建议尽量不要使用小程序中的生命周期的钩子函数
2、小程序不支持路由,因此,路由跳转使用小程序的页面导航 api 代替
this.$router.push –> wx.navigateTo() // 进入子页面
this.$router.replace –> wx.reLaunch()// 打开新页面
3、微信小程序的页面的 query 参数是通过 onLoad 获取的,mpvue 对此进行了优化,直接通过 this.$root.$mp.query 获取相应的参数数据
4、小程序里所有的 BOM/DOM 都不能用,因此 v -html、v-text 不能用
5、图片 src=”{{}}” => :src=””, 才能生效
6、不支持部分复杂的 JavaScript 渲染表达式,mpvue 会把 template 中的 {{}} 双花括号的部分,直接编码到 wxml 文件中,由于微信小程序的能力限制 (数据绑定),所以无法支持复杂的 JavaScript 表达式。建议使用 computed
目前可以使用的有 + – * % ?: ! == === > < [] .,剩下的还待完善。
7、不支持 filter 过滤器,因为小程序 wxml 不支持
8、列表渲染需要注意一点,嵌套列表渲染,必须指定不同的索引 :key=””
9、不支持在 template 内使用 methods 中的函数,即 <div>{{handleFn()}}</div>
10、获取当前页面地址
this.$route.fullPath –> getCurrentPages()[0].route
11、不支持本地图片用作背景图,可以使用网络图片、或者 base64,或者使用 img、image 标签
12、上拉加载 / 下拉刷新,选用小程序的全局 api; scroll-view 中无法使用
13、不支持 css 媒体查询,css 样式避免标签选择器,不易于维护,尽量使用类选择器
14、CSS 样式导入,使用 @import url(“”)
15、暂不支持在 组件
上使用 Class 与 Style 绑定
参考:
mpvue 文档
用 Vue.js 开发微信小程序:开源框架 mpvue 解析
mpvue 小程序开发 – 生命周期梳理
美团小程序框架 –mpvue 入坑指南
mpVue 的使用小技巧之跳转与传参
小程序开发框架 WePY 与 mpvue
px2rpx-loader 使用和源码分析