微信小程序底层框架实现原理

Youky ... 2023-2-24 前端
  • 微信小程序
About 4 min

# 微信小程序底层框架实现原理

# 双线程架构

# 是什么?

渲染层与逻辑层分别由两个线程管理,两个线程之间由 Native 层进行统一处理

  • 渲染层的界面使用 webview 进行渲染。
    • 多个页面对应多个 webview(最多 10 个)
  • 逻辑层采用 JSCore 运行 JavaScript 代码
    • 只有一个 APP 实例,所有 JS 都在一个线程中执行

# 对比单线程的优势

  • 避免单线程中,由于资源加载而阻塞页面解析
  • 将微信 SDK、底层基础库等放在 Native 层中,减少应用的包体积和网络请求数量

# 如何解决 H5 的安全问题

  • 禁用部分 JS 操作:如获取 DOM、动态执行 JS 代码等
  • 为 JS 提供沙箱环境

# 为什么需要提供 WXS

双线程架构中,频繁的跨线程通信(如手势识别等)会带来性能问题。

WXS 可以在渲染层中写部分逻辑,在渲染层单独处理部分频繁改变的数据

# PageFrame

# 解决的问题

每个页面都是一个独立的 webview,其中包含大量内容。小程序如何快速新建并打开页面

# 原理

作为一个模板,其中引用了各种通用资源 js,当多次使用 PageFrame 创建页面时,这些自资源会使用本地缓存。

启动一个页面的步骤:

  1. 启动一个 webview
  2. 在其中初始化基础库,以及一些基础库的内部优化
  3. 注入 WXML 和 WXSS,小程序能在接收到页面初始数据之后马上开始渲染

# Exparser

Exparser 是小程序的组件组织框架,基本参考 Shadow DOM 模型实现。

Shadow DOM 是 WebComponents 中的概念,它允许将隐藏的 DOM 结构附加到常规的 DOM 树中。可以选择外界是否能获取内部结构。

一些 HTML 标签是浏览器内置的 ShadowDOM 组件,如 video、button 等。这些组件不能作为 ShadowDOM 的宿主元素。

Shadow DOM 中的事件,可以选择是否冒泡至宿主元素外部

# WXSS

# 和 css 的区别

  • 新增了 rpx 单位,换算方式为屏幕宽度为 375px 时,1px = 2rpx
  • 仅支持部分选择器

# 编译方式

WXSS 文件不在 webview 中渲染,而是提前编译为 js。 在该 js 中,会获取设备信息(屏幕宽度等),并以此将 rpx 转换为实际像素。 该 js 内容会通过 eval 进行执行,并最终生成 style 标签插入到页面中。

# WXML 的编译:Virtual DOM

WXML 代码也会编译为 JS,生成一个 $gwx 函数,它用于生成虚拟 DOM。

$gwx 的返回值也是一个函数 generateFungenerateFun 接受模板渲染所需的动态数据,其返回值是虚拟 DOM 树。

# 事件通讯系统

# 通信机制

  • 渲染层和逻辑层的通信都是通过 Native 层转发
    • iOS 是利用了 WKWebView 的提供 messageHandlers 特性
    • 安卓是往 WebView 的 window 对象注入一个原生方法
  • 在微信中封装为 WeiXinJSBridge 作为兼容层
  • 在开发者工具中则是用 Websocket 对通信方法进行模拟

# 绑定事件

标签的所有 attribute 都会存在虚拟 DOM 的 attr 属性中,通过正则判断某个属性是否是绑定的事件,找出事件后为节点注册事件。

小程序的事件都是和 HTML 原生事件对应的(如 tap 对应 mouseup),最终会通过 window.addEventListener 添加原生事件的监听

# 触发事件

渲染层中,会拼接事件的 event 参数,创建一个 exparser 事件并触发,通过 sendData 向逻辑层通信

# 数据在两个线程中的传递

页面加载时,data 中的数据会以 JSON 字符串的形式传至渲染层。因此,data 中的数据必须是可以转为 JSON 的类型

字符串,数字,布尔值,对象,数组

# 小程序框架

# 预编译

自己定义一套 DSL(语法规则),一般对应 React 或 Vue,通过源码的 AST 解析还原为微信小程序原生代码的形式

局限性:

  • React 或 Vue 出了新的语法,要兼容的话要扩展 DSL
  • 兼容问题,小程序不支持的属性,无法编译

# 半编译半运行时:mpvue

基于类 vue 语法

  • 将 template 编译为 wxml
  • 在 vue 的 patch 流程中,不更新 DOM,而是触发 setData 来更新视图

# 纯运行时框架:remax

# 纯运行时框架要解决的问题

React / Vue 的视图更新都是基于 DOM API 的,但小程序的逻辑层并未提供任何操作节点的 API。

唯一的更新视图的方式,就是 setData

# 解决思路:动态 template

WXML 提供模板(template) (opens new window),可以在模板中定义代码片段,然后在不同的地方调用。

使用模板时,通过 is 属性决定要使用的模板是哪一个。而 is 属性的取值可以用双括号语法从而使用 data 中的数据。

因此,实现的思路就是:

  • 在 data 中实现一个类似虚拟 DOM 的对象,通过递归的形式在 WXML 中将其渲染为 template
  • 更新 DOM 的流程,改为更改这个虚拟 DOM 对象,通过 setData 触发视图更新
Last update: February 24, 2023 00:18
Contributors: Youky