跳到主要内容
版本:1.0.0

整体架构

关于小程序转 WebMorJS 的思路是将已有的小程序工程直接转成 react 代码。

借助 WebpackMorJS 提供了一整套的 loader,包含 axml-loader(负责编译 axml 文件)、acss-loader (负责编译 acss 文件)、js-loader (负责编译小程序中的 js 文件)。

介绍

上图是小程序转 Web 的整体架构。最上面的四个部分属于内核部分,分别由 编译器、dsl runtimeapi(my)、全局公共组件 四个模块组成,每一个模块都是独立可替换的。

Compiler(编译器)

编译器模块比较特殊,主要负责将 axmlacssjsjson 等文件编译成模块化的 js 文件,完全是静态编译。至于详细的编译流程下面会细说。

Runtime(运行时)

runtime 提供了小程序在 h5 中的运行时功能,比如:PageComponentApp 等全局函数,并且提供完整的生命周期实现,事件的注册、分发(非 react 事件),slotblock 等全局 DSL 层面组件实现,等等。

API (my.*)

api 模块提供了类似支付宝小程序中 my 一系列 api 的实现,事实上,目前 api 的实现其实相对来说比较分散,在 api 模块并没有提供全部的支付宝小程序 api 的实现,有些 api 在全局公共组件来实现(比如:UI 相关的 api,下拉刷新等),甚至有些 api 需要开发者自己来实现(比如:登录态的获取等等)。

Components(全局公共组件)

全局公共组件指的是 viewbuttonscroll-view 等支付宝小程序原生提供的组件,在 MorJS 中,这些组件是基于 web-components 来实现的,之所以使用web-components 来实现而不是 react ,是基于以下几个原因:

  1. web-components 组件支持标签选择器。这样就能跟小程序的组件在样式的写法上抹平。
  2. web-components 组件天生具备样式隔离的能力,不用担心公共组件的样式会污染全局。
  3. web-components 组件支持路由事件,因此小程序中的 tap、touch 等事件可以直接自定义抛出。
  4. web-components 组件注册以后,可以像原生 html 组件一样使用,无需单独引入组件文件。支持原生 html 组件的所有方法和功能。

以上几个优势,如果用 react 来实现,虽然在技术上是可以有类似的实现,但是成本就比较高了,远不如 web-components 来的简单直接。

其它

除了以上四个核心模块以外,由于公共组件、api 的实现都是模块化的,尤其是 api 部分,可以轻易的扩展自己的 api 设置重写部分 api ,因此对于接入方来说,可以扩展自己的公共组件、api

以上四个模块,除了编译器模块,剩余的三个模块整体才算一个完整的小程序运行时。

运行流程

上图中的运行流程其实跟普通的 react 工程运行流程差不多,仅仅是多了一个针对 axml 文件、acss 文件、js 文件进行额外编译转码的流程,最终还是通过 webpack 进行打包运行。

进一步可以分析出,这样的流程,不管是整个工程级别的转码,还是组件库级别转码,甚至是已有 react 项目直接引入小程序代码,都可以通过上图中的方式直接(loader 方式)或者间接(命令行)运行。

编译流程

不同的文件,编译的方式是不一样的,编译处理的内容也是不一样的。

AXML

整个小程序编译器,就数 axml 文件编译最复杂。整个编译流程如上图所示。

  1. axml 文件转换成 xml 文档树。
  2. xml 文档树,转换成小程序的 ast。针对不同的节点类型、属性类型做针对性的转换。 3, 将小程序 ast 进一步转换成 jsx 代码。事实上,有了 ast 以后,不仅能转换成 react 代码,也可以转换成 vue 的代码,甚至也可以转换成基于 web-component 的代码,只需要提供不同的 dsl runtime 即可。这也是为何上面将 dsl runtime 单独作为一个模块的原因。

axml 编译复杂的部分,不在 ast,而是在 generate 中,各种小程序 dsl 的特性都需要在这里进行转换。

ACSS

acss 编译,其实很简单,目前只是做了三件事:

  1. rpx 支持—— 将 rpx 编译成 rem
  2. 标签样式的支持—— 这是这里仅仅是做了一个标签样式的重命名。之所以这样,是因为在实际 axml 中用的标签比如 view ,但是实际注册的标签名是 tiga-view 。因此在acss 文件中,对标签样式的重命名也是一个非常重要的功能。
  3. 条件编译。

JS

最新版本的编译器,极大简化了 JS 文件的编译。目前 JS 编译器仅仅如如下几个事情。

  1. 条件编译的支持。
  2. import 路径的转换。主要是将绝对路径转换成相对于工程的根目录的相对路径。
  3. Page 或者 Component 相关的 js 文件,编译成以 export default 形式导出的 js 文件,之所以这样做,是因为需要将 Page 或者 Component 全部以组件的形式导出,然后可以被其他 js 文件引用。

总结

但从编译器来说,最核心的就是 AXML 的编译,而 acssjs 文件 的编译相对来说很简单。