小程序跨端方案概述

2,324 阅读5分钟

小程序跨端技术是编译时运行时的结合方案。

编译时和运行时是软件开发里面用于描述两个不同的软件开发阶段。开发一个程序,程序员首先需要写源代码(Source Code) ,来完成程序的功能。源代码需要被编译成机器可以识别的程序,这个编译过程被称为编译时。用户可以运行编译过的程序,程序运行的过程被称为运行时。

小程序多端方案主要有:

  • 类 Vue 风格框架
  • 类 React 风格框架
  • 自定义 DSL 框架

编译时的方案

以类Vue风格为例,思路类似将Vue的template使用vue-loader转换成浏览器可识别的代码,小程序则是讲Vue 模版编译为小程序 .wxml 文件。具体的过程是将开发者的代码解析成AST树,再修改AST树,最后将修改后的AST转换成小程序代码。而这种代码转换并不是适用将<script>代码转换成小程序代码。要将<script>与小程序想结合,需要使用我们的运行时方案。

运行时方案

对于一段 Vue 代码,我们通过响应式理念监听数据变化,触发视图修改,放到小程序中,多端方案要做的就是监听数据变化,调用 setData() 方法,触发小程序渲染层变化。

一般在 Vue 单文件组件的 script 部分,我们会使用以下代码:

new Vue({
  data() {},
  methods: {},
  components: {}
})

来初始化一个 Vue 实例。对于多端方案来说,就完全可以引入一个 Vue 的运行时版,对上述代码进行解析和执行。事实上,mpvue 就是 fork 了一份 Vue.js 的代码,因此内置了 Vue runtime 能力,同时添加了小程序平台的支持。

具体还需要做哪些小程序平台特性支持呢?举一个例子,以微信小程序为例,微信小程序平台规定,小程序页面中需要有一个 Page() 方法,以生成一个小程序实例,其中 Page() 方法是小程序官方提供的 API。

那么对于业务方写的new Vue()代码,多端平台要手动执行微信小程序平台的 Page(),完成初始化处理.

//小程序单文件组件的script部分
new Vue() {}

// 多端方案
new Vue() {}
// 在Vue实例化时,会调用Vue.init()
Vue.init = () => {
// 魔改Vue.init方法,保留原始逻辑并调用小程序的Page()方法
  Page()
}

该多端方案内置了 Vue 运行时版,并实例化了一个 Vue 实例,同时在初始阶段调用了小程序平台的 Page() 方法,因此也就有了一个小程序实例。

  • vue 原本的运行时

  • 结合小程序的运行时,不是做dom-diff,而是调用小程序的setData,同时在创建页面的Vue实例的同时要创建对应的小程序Page实例

下面的工作,就是在运行时将 Vue 实例和小程序实例进行关联,以做到:数据变动时,小程序实例能够调用 setData() 方法,进行渲染层更新。

思想确立后,如何实施呢?首先这就需要你对 Vue 原理足够清楚了:Vue 基于响应式,对数据进行监听,在数据改动时,新生成一份虚拟节点 VNode。接下来对比新旧两份虚拟节点,找到 Diff,并进行 patch 操作,最终更新了真实的 DOM 节点。

因为小程序架构中,并没有提供操作小程序节点的 API 方法,因此对于小程序多端方案,我们显然不需要进行 Vue 源码中的 patch 操作。又因为小程序隔离了渲染进程(渲染层)和逻辑进程(逻辑层),我们不需要处理渲染层,只需要调用 setData() 方法,更新一份最新的数据就可以了。

因此,借助 Vue 现有的能力,我们秉承“数据部分让 Vue 运行时版接手,渲染部分让小程序架构接手”的理念,就能实现一个类 Vue 风格的多端框架。当然,整个框架的设计还要考虑事件处理等模块,我们就不再具体展开。

至此,编译时和运行时方案组合在一起,我们就实现了一个类 Vue 风格的小程序多端框架的技术方案架构。

小程序的架构

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

小程序的渲染层和逻辑层分别由2个线程管理:渲染层的界面使用了 WebView 进行渲染;逻辑层采用 JsCore 线程运行 JS 脚本。一个小程序存在多个界面,所以渲染层存在多个 WebView 线程,这两个线程的通信会经由微信客户端做中转,逻辑层发送网络请求也经由 Native 转发,小程序的通信模型下图所示。 img

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