小程序框架原理之渲染流程及通信流程

MINA

MINA 是在微信中开发小程序的框架。其目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。

MINA 提供了自己的视图层描述语言 WXMLWXSS,以及基于 JavaScript 的逻辑层框架,核心是一个响应的数据绑定系统。整个系统分为视图层(View)和逻辑层(App Service),并在视图层与逻辑层间提供了数据传输和事件系统,可以让开发者可以方便的聚焦于数据与逻辑上。

MINA 让数据与视图保持同步非常简单。当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。MINA 是腾讯给微信小程序命名的框架,实际上应用的是目前业界最著名的 MVVM 模式。

技术图片

wxml的真实面目

我们都知道小程序提供了很多方便快捷的自定义组件(标签),但你知道小程序的这些组件编译过后会渲染成什么吗?先说答案,其实 wxml 经过编译后会渲染成 html 。很简单的一点,你发现在小程序内编写 html 标签,最终也可以运行。

探寻

光说可能体会不到,下面开始探寻小程序真实渲染的样子。先看下开发者工具内 wxml 的内容,待会和真实渲染的内容做对比。

技术图片

接下来一步步找到小程序 wxml 渲染完成的真实样子,工具菜单栏点击微信开发者工具,选择调试微信开发者工具。打开的控制台可以调试整个微信开发者工具,用调试箭头指向小程序内容区域,这时可以看到小程序视图层是被嵌套在 webviewiframe 内。

技术图片

但是当我们点开 iframe 是无法查看到里面内容的。如果想要查看调试 webview,只需选中 webview 打开它的调试工具即可,在控制台输入以下代码:

$$(‘webview‘)[0].showDevTools(true)

可以看到又打开了一个调试窗口,这里面就是小程序视图层渲染的真实样子:

技术图片

可以看到结构和 wxml 里的内容几乎一模一样,只是 topbar 变成了 wx-topbarview 变成了 wx-view等。这些都是内部实现的一套对应小程序标签的 webComponent 组件,而 webComponent 实际渲染出来还是 html 标签。

转换过程

转换过程是微信开发者工具内部通过一个可执行编译工具实现对小程序文件转换。在微信开发者工具控制台输入 openVendor() 会打开一个文件夹,里面存放着微信的基础库及工具,在里面可以找到 wcc.exewcsc.exe 执行文件,分别对应 wxmlwxss 的文件转换。

该工具可以单独对小程序文件进行转换,使用方法 ./wcc -d wxml文件路径 >> 输出路径。例如,将工具复制到一个文件夹内,再将一个 wxml 放入该文件夹内,命令行输入 :

./wcc -d index.wxml >> index.js

可能有人很好奇为什么是生成 js 文件,而不是 html 文件。原因很简单,因为需要处理 wxml 的动态绑定数据。看看这个 js 文件生成的是什么:

技术图片

因为这些都是混淆压缩过的代码,基本没有可读性。这里只需要注意一个函数就好,那就是 $gwx。这是个很关键的函数,它的作用是生成虚拟dom树,用于渲染真实节点。

接下来回到 webview 调试窗口,在 head 内找到这段插入的 script 标签代码:

技术图片

有没有很熟悉,没错,就是和上面转换后的代码是同一个东西。也就是说,我们的 wxml 文件通过编译,最终在视图层中执行的就是这段 js 代码(这里只是可以大概这么理解,实际需要向逻辑层获取数据才能渲染页面)。控制台输入 $gwx 发现这个函数存在,那么这个函数如何生成虚拟dom呢?$gwx 函数的第一个参数接收一个路径参数,这个路径就是 wxml 文件路径,此时在控制台输入:

let generateFunc = $gwx(‘./pages/index/index.wxml‘)
generateFunc()

这时页面虚拟dom就生成出来了:

技术图片

单纯调用 generateFunc 生成出来的虚拟dom是没有动态绑定数据的,如果想要动态的绑定数据,在调用 generateFunc 时传入一个数据对象。但是数据全在逻辑层里,这时就需要进行通信了。

数据通信

首先要知道小程序时运行在基础库之上的,但它们都是压缩打包好的,后面找到反编译出来的基础库代码,其中最重要的就是 WAService.jsWAWebview.js,它们分别是视图层和逻辑层的核心实现。

它们之间需要一个桥梁来进行通信,那就是 JS BridgeJS Bridge 提供调用原生功能的接口(摄像头,定位等),它的核心是构建原生和非原生间消息通信的通道,而且这个通信的通道是双向的。通过 JS Bridge 的发布订阅方法,视图层和逻辑层进行数据通信。

通信流程

接下来看看视图层和逻辑层的交互流程:

  1. wxml 转换成对应的
    js 文件,等待生成虚拟dom函数
    $gwx 准备完成,使用
    dispatchEvent 通知
    WAWebview
技术图片
  1. WAWebview 监听到
    generateFuncReady 事件触发,使用
    WeixinJSBridge.publish 向逻辑层通信。
技术图片
  1. 逻辑层处理逻辑,也就是我们平常写的小程序 js 文件里的东西,然后通过 JS Bridge 通知并返回数据给视图层。

  2. 视图层接收到数据,将数据传入生成虚拟dom的函数内,渲染页面,当然小程序也有相应的diff算法。

例如在 wxml 中绑定一个动态数据 title,视图层接收到数据后,重新生成虚拟dom

generateFunc({
  title‘标题‘
})
  1. 初始化完成后,就会走对应的其他生命周期,或者用户触发事件,数据都会在逻辑层处理完成后通过
    JS Bridge 通知到视图层,视图层再次调用生成虚拟dom的函数,更新页面。

wxss如何工作

wxss 工作原理和 wxml 差不多,都是通过工具转换为 js。为什么又是转换成 js,因为有 rpx 单位,需要根据手机尺寸进行设置 px

wcsc.exe 转换命令如下:

./wcsc -js index.wxss >> index.js

可以看到文件开头就是对 rpx 的转换

技术图片

之后创建 style 标签,动态添加到视图层中

技术图片

最后

附上 WAService.js 和 WAWebview.js 的代码作为学习参考。

相关文章