微信小程序mpvue框架总结
原理架构
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使用和源码分析