深入浅出主流的几款小程序跨端框架原理

2023-11-17

目前,小程序在用户规模及商业化方面都取得了极大的成功。微信、支付宝、百度、字节跳动等平台的小程序日活都超过了3亿。

我们在开发小程序时仍然存在诸多痛点:小程序孱弱简陋的原生开发体验,注定会出现小程序增强型框架,来提升开发者开发体验;各家厂商小程序API碎片化的现状,注定会有多端框架会成为标配,由跨端框架肩负跨平台移植挑战。

正是因为开发者对于提升小程序开发效率有着强烈需求,小跨端框架发展到如今已经百花齐放、百家争鸣:除了美团的 mpvue 、网易的 megalo 、滴滴的 chameloen 已经趋于稳定,京东的 Taro开始探索 taro next, Hbuilder 的uni-app 产品和生态持续完善,微信新推出了支持H5和微信小程序的 kbone 框架,蚂蚁金服的 remax

上述的这么多跨端框架纷繁复杂,我们可以从下面两个维度进行分类:

小程序跨端框架的分类

按语法分类

从框架的语法来说,可以分为下面两类:

  • Vue 语法
  • React 语法 / 类 React 语法

主流的跨端框架基本遵循 React、Vue 语法,这也比较好理解,可以复用现有的技术栈,降低学习成本。

remax Taro next Taro 1/2 megalo mpvue uni-app chameloen
语法 react react 类 react (nerve) vue vue vue 类 vue
厂家 蚂蚁金服 京东 京东 网易考拉 美团 Hbuilder 滴滴

按实现原理分类

从实现原理上,开源社区的跨端框架大致分为下面两类:

  • compile time** 编译时**
  • runtime** 运行时**

compile time** 编译时**的跨端框架,主要的工作量在编译阶段。他们框架约定了一套自己的 DSL ,在编译打包的过程中,利用 babel 工具通过 AST 进行转译,生成符合小程序规则的代码。

这种方式容易出现 BUG ,而且开发限制过多。早期的** Taro 1.0 和 2.0** 的版本就是采用的这种方案,下文会有更具体的介绍。

而另外一种runtime** 运行时模式**, 跨端框架真正的在小程序的逻辑层中运行起来 React 或者是 Vue 的运行时,然后通过适配层,实现自定义渲染器。这种方式比静态编译有天然的优势,所以 Taro 的最新 Next 版本和 Remax 采用的是这种方案。

写在小程序跨端原理之前

通过上文我们知道小程序跨端框架目前有很多嘛,各个大厂都会有自己的一套,百花齐放。文章篇幅有限,如果要分别拆开讲清楚他们各家实现的细节,是一件很困难同时很费时间的事情。

所以,下文会尝试梳理一下主流小程序一些共用性的通用实现原理, 尽量会屏蔽忽略掉各家实现一些细枝末节的细节差异,也不会在文章中贴大段的源码分析,而是通过伪代码来代替。

下面,我们会从** Vue 跨端框架**和 React 跨端框架两个大方向,进入到小程序跨端原理的世界,讲解这些跨端框架的核心原理,深入到源码底层去分析,揭开他们神秘的面纱。

Vue 跨端框架

当你使用 megalompvue 这些 Vue 跨端框架时,看上去,我们写的是vue 的代码,然后打包编译之后就可以运行在小程序内,是不是很神奇?这些框架背后做了哪些事情呢?

实际上,这些** Vue的跨端框架 **核心原理都差不多,都是把 Vue 框架拿过来强行的改了一波,借助了 vue 的能力。比如说,vue 的编译打包流程(也就是vue-loader的能力), vue 的响应式双向绑定、虚拟dom、diff 算法。上面这些东西跨端框架都没有修改,直接哪来用的。

那么哪些部分是这些跨端框架自己新加的东西呢?

涉及到 Vue 框架中操作DOM节点的代码

这些跨端框架,把原本Vue框架中原生 javascript 操作 DOM 的方法,替换成小程序平台的 setData()。为什么要这样呢?不着急,下文会有比较详细的讲解。

不着急,慢慢来,我们先从一个最简单的问题开始。

vue 到 小程序

首先我们来看,一个 vue 的单文件,究竟做了啥,怎么就能跑在小程序里面了?

我们知道,对于微信小程序来说,需要有 4份文件:.wxml.wxss.js.json

img

(上面是去微信小程序官网截的图)

而对于一个 Vue 组件来说,一个 vue 文件有三个部分组成:template, script, style

那么,这些跨端框架把Vue 单文件中的 <template><script><style>这三个部分对应的代码,拆一拆,分别处理编译一下,分到 .wxml.wxss.js.json 这 4 份文件中,如下图所示:

img

我们分别从<template><script><style>这三个部分来讨论:

  • <style> 部分是最简单的。一般来说,在 h5 环境中的 css 样式,大部分都可以直接挪到 .wxss,需要处理的部分比较少,除了少部分不支持的属性和 小程序的单位转换
  • <template>** 转换到 **.wxml稍微复杂一点。我们需要把 h5 的标签啊、vue特殊的语法替换成小程序的标签、小程序特殊的语法。替换的工作我们称为 模板替换 ,下文会有一个章节用来介绍。
  • 最难的是<script>** 到 **.js, 涉及到 vue 的运行时 如何和 小程序的实例通讯的问题,这一部分会用比较多的章节去介绍。

img

接下来,我们先看模板替换 ,也就是template 生成 .wxml 文件的过程。

<template>.wxml

Vue 是采用 template 语法的,各大厂商的小程序也是采用了 template 语法。从 Vue 的 template 转变成小程序的 template 相对比较简单,React 的 jsx 转变为小程序的 template 就相对比较棘手啦。

Vue 的 template 与小程序的 template 大体是上的语法很类似,但是还是有不一样的地方。例如小程序里没有 <div> 标签,而是小程序厂商提供的 <view> 标签等等

因此我们需要把 Vue 模版转换为微信小程序的 .wxml

img

例如上图所示,<div> 标签需要转换成 <view> 标签,一些 vue 中的语法也需要进行转化成对应小程序平台的语法。

再比如说,在 Vue 里面绑定事件常用 @methodName 的语法, 转成小程序模版则需要用 bind,同时用 tap 事件替换 click 事件。

img

除了这个,还有一些vue的模板语法,也需要转成小程序的模板语法

img

Vue 和小程序插值表达式则是一样的,采用了双花括号,可以不需要做任何转化

img

上面展示的这些模板替换,都只是替换为微信小程序的语法。转化为其他小程序平台的语法也是类似的思路,如下图所示:

img

那么,模板的转化具体是如何实现的呢? 我们的第一想法是通过正则来匹配,但是要写出匹配出所有情况的正则是非常困难的。

实际上,mpvuemegalouni-app 的框架是采用了 ast 来解析转化模板的。

img

模板替换过程其实就是两侧对齐语法的过程,把语法不一致的地方改成一样的,是一个 case by case 的过程,只要匹配到不同情况下语法即可,比较费功夫但是难度系数不是很高。

接下来我们看如何把 <script> 中的内容,挪到小程序 .js 中呢?

<script>.js

我们在 .vue 单文件中的 script 部分中, 通常会写下面的代码,我们会写一个 Vue 的配置项对象传入到 Vue 构造函数中,然后 new Vue() 会实例化出来一个 vue 实例。

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

上面的代码是完全可以跑在小程序的逻辑层里面的,只要引入vue 即可,毕竟 Vue 大部分就是纯粹的 javascript。也就是说,小程序的渲染层里面是完全可以直接运行起来 Vue 的运行时和 React 的运行时的。

但是这样还不够,小程序平台还规定,要在小程序页面中调用 Page() 方法生成一个 page 实例, Page() 方法是小程序官方提供的 API。

在一个小程序的页面中,是必须有 Page() 方法的。微信小程序会在进入一个页面时,扫描该页面中的 .js 文件,如果没有调用 Page() 这个方法,页面会报错。

如下图所示,我们在 <script> 中写的是 new Vue() 这样子的代码,而微信想要的是 Page()

img

那么,应该怎么解决呢?

Vue 跨端框架他们拓展了 Vue 的框架,把 Vue2.0 的源码直接拷贝过来,改了里面的初始化方法,在初始化方法中调用了 Page() 方法, 如下面伪代码所示:

new Vue() {};
Vue.init = () => { 
  // 在 vue 初始化的时候,调用了 page() 方法
  Page()
}   

在 vue 实例化的时候,会调用 init 方法,在 init 方法里面会调用 Page() 函数,生成一个小程序的 page 实例。

这样,我们在一个小程序页面中,就会同时存在一个 vue 的实例,和一个小程序的 page 实例,他们是如何融合起来一起工作的呢?他们之间是如何做到数据管理的? 如何进行通讯的呢?

接下来就涉及到 Vue 框架的核心流程了,为了方便一些不了解 Vue 同学,同时也为了更好的深入理解下面讲的内容,接下来会稍微讲一丢丢 vue 的核心流程。

  • ( 真的只有一丢丢 )

img

Vue 的核心流程

如下图左侧所示,简单来说, 一个 .vue 的单文件由三部分构成: template, script, style

img

我们先看上图中的橙黄色的路径,也就是 template 部分的处理过程。

如下图所示,template 模板部分会在编译打包的过程中,被 vue-loader 调用 compile 方法通过词法分析生成一个 ast 对象,然后调用代码生成器,经过遍历 AST 树递归的拼接字符串操作,最终生成一段 render 函数, render函数最后会存在打包生成的dist 文件中。

img

可以看下面这个例子,一段简单的 template 模板如下所示:

<div class="ctl-view" @click="handleClick">
  {{ a }}
</div>

经过编译之后,通过 ast 进行分析,生成的 render 函数如下:

_c("div", 
    { staticClass: "ctl-view", on: { click: _vm.handleClick } },
    [_vm._t("default")]
)

render 函数会在第一次 mount时,或者Vue 维护的 data 有更新产生的时候会被执行。

那么执行下面这段 render 函数会拿到什么呢?

img

上面图中蓝色圆圈中的 _c 方法是创建元素类型的vnode, 而 _v 方法是创建 文本类型的vnode。

Render 函数中会调用这些方法创建不同类型的vnode,最终的产物是生成好的虚拟DOM树 vnode tree,对应上面图中 render 函数的下一个阶段 vnode。

虚拟DOM树是对真实DOM树的抽象,树中的节点被称作 vnodevnode 有一个特点, 它保存了这个DOM节点用到了哪些数据 ,这一点非常重要。

Vue拿到 虚拟dom树之后,就可以去和上次老的虚拟dom树patch diff 对比。

img

这一步的目的是找出,我们应该怎么样改动现存的老的DOM树,代价才最小。

patch 阶段之后,如果是运行在浏览器环境中, vue 实例就会使用真实的原生 javascript 操作DOM的方法(比如说 insertBefore , appendChild 之类的),去操作DOM节点,更新浏览器页面的视图。

接下来,我们再来看一下上面图中,蓝色的线条的路径。

img

在new Vue 的时候,Vue 在初始化的时候会对数据 data 做响应式的处理,当有数据发生更新时,最后会调用上文的 render 函数,生成最新的虚拟DOM树。

接着对比老的虚拟DOM 树进行 patch, 找出最小修改代价的vnode 节点进行修改。

上面介绍的流程就是 vue 的整体流程啦。

(如果有不理解的地方,不重要,也不需要担心会阻塞下文的阅读)

我们要关心的是,下面的类 vue 小程序跨端框架的核心流程。接下来一起来看吧。

类 vue 小程序跨端框架的核心流程

在进一步讲解之前,我们先思考一个问题。上图中,Vue 在 diff 之后就是操作了原生的 DOM 元素,但是各家厂商的小程序不支持原生DOM操作,因此也就没有修改视图节点的能力。那么我们怎么样才能更新小程序的视图呢?

下面这张图代表了类 vue 小程序跨端框架的核心流程图。

img

咋一看这张图,会发现和上面Vue的图是很像的。毕竟 megalompvue 等小程序框架,本质都是对 vue 的拓展(copy过来改了改)

仔细和上面的 vue 的核心流程图一对比,我们发现,小程序跨端框架的流程图替换掉 vue 原本的 DOM 操作,替换为新增的绿色的setData 操作, 同时还多了一个绿色框框中的的 Page() 方法。

Page() 方法上文有介绍过原因

setData() 是小程序官方提供的 API,用来修改小程序 page 实例上的数据,从而会更新小程序的视图。

『替换掉 vue 原本的 DOM 操作』这一个点比较容易理解,因为小程序容器并没有提供操作小程序节点的 API 方法,这是因为小程序隔离了渲染进程 (渲染层)和逻辑进程 (逻辑层),如下图所示:

img

在小程序容器中,逻辑层到渲染层的更新,只能通过 setData() 来实现。

不管是 mpvuemegalo ,还是uniapp,这些类 vue 跨端框架,都是通过这种方法来更新视图的。而且,在未来可预见的几年里,只要小程序厂商不提供修改小程序节点的 API 方法,小程序跨端框架更新 DOM 节点仍然会通过 setData 这种 API

好了,到了这一步,我们已经知道了,跨端框架替换了 Vue 框架中 **JS 操作DOM 原生节点的 API **为 **setData() **来更新小程序的页面。

但是我们还是不知道具体背后做了什么,接下来,看一个具体的例子:

new Vue({
  data(){
    return {
      showToggle: true
    }
  }
})


// 下面是经过 模板替换 之后的代码
<view wx:if="{{showToggle}}">
</view>

在上面的例子中,showToggle 这个变量代表的数据是维护在Vue 实例上的。

在页面初始化的时候,我们的小程序跨端框架就开始执行了,它会先实例化一个Vue 实例,然后调用小程序官方的 Page() API 生成了小程序的page 实例,并在在 Vue 的 mounted 中会把数据同步到小程序的 page 实例上。

因此在实际页面打开之后,会同时存在小程序原生的Page 实例和 Vue 实例。vue 实例上有数据(我们的 data 本来就是定义在 vue 里面的),小程序Page 实例上也有数据(小程序实例上没数据没法渲染页面对吧)。

当 Vue 中的数据发生变化时,会触发 Vue 响应式的逻辑,走 上图中Vue 更新的那一套逻辑:重新执行 render 函数

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入浅出主流的几款小程序跨端框架原理 的相关文章

  • vue2中Cascader 级联选择器限制选择个数和回显问题

    文章目录 1 组件默认数据绑定 2 指定数据绑定 3 watch监听v model绑定的数组 控制选中个数 4 前后端数据转换 实现回显 1 接口初始数据回显 2 重新选择级联选择器后 如何将选择的数据转换成后端需要的数据 3 最后提交数据
  • filezilla中文目录乱码怎么解决

    FileZilla是一款常用的文件传输工具 但在使用过程中可能会遇到乱码的问题 以下是一些可能的解决方案 设置字符集 在连接上站点后 点击菜单栏的 文件 选项 下拉选择 添加当前连接到站点管理器 在弹出的 站点管理器 窗口中 左侧选择 新站
  • 1-创建小程序项目

    注册 打开https mp weixin qq com 点击 立即注册 选择小程序 获取APPID 登录小程序在 开发管理 gt 开发设置 获取 APPID 开发工具 登录小程序在 开发工具 gt 开发者工具 获取 微信开发者工具 创建小程
  • vue的组件

    在Vue中 组件是可复用的代码块 用于构建用户界面 Vue的组件系统允许您将界面拆分为独立的 可重复使用的部件 提供了更好的代码组织和复用性 以下是在Vue中创建组件的基本步骤 创建一个组件实例 可以使用Vue extend 方法创建一个V
  • 事件委托Tab栏切换

  • Fiddler工具 — 9.命令行和状态栏

    1 命令行 命令行在Fiddler的左下方的黑色窗口 也叫 QuickExec 可以调用 Fiddler的内置命令 这一系列内置的函数用于筛选和操作会话列表中的session 会话 虽然它不是很显眼 但用好它 会让你的工作效率提高 N 倍
  • Firefox浏览器-渗透测试插件推荐

    在日常工作中可能需要一些浏览器插件辅助我们做工作 下面是比较好的 当然不一定对你有用 找到适合自己的即可 FoxyProxy FoxyProxy是一个高级的代理管理工具 它完全替代了Firefox有限的代理功能 它提供比SwitchProx
  • 【连续和自适应资源需求估计】通过不断应用在线优化、选择和估计,SARDE能够有效地适应在线跟踪,并使用得到的集成技术减少模型误差(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据 文章
  • Web 安全漏洞之 OS 命令注入

    什么是 OS 命令注入 上周我们分享了一篇 Web 安全漏洞之 SQL 注入 其原理简单来说就是因为 SQL 是一种结构化字符串语言 攻击者利用可以随意构造语句的漏洞构造了开发者意料之外的语句 而今天要讲的 OS 命令注入其实原理和 SQL
  • 微信小程序的自动化测试框架

    微信发布了小程序的自动化测试框架Minium 提供了多种运行验证方式 其特点 支持一套脚本 iOS Android 模拟器 三端运行 提供丰富的页面跳转方式 看不到也能去得到 可以获取和设置小程序页面数据 让测试不止点点点 可以直接触发小程
  • 【计算机毕业设计】毕业生就业管理微信小程序_lm9q0

    腾讯公司在2017年1月19日发布了一款不需要下载 不需要卸载 不需要存储的软件叫微信小程序 受到了很多人的喜欢 微信小程序自2017年发布至今 依托微信的社交属性和庞大的用户基数 已经渗透到生活的方方面面 1 微信小程序可以将基于微信平台
  • 【计算机毕业设计】北京医疗企业固定资产管理系统的设计与实现 _4c4c1

    近年来 人们的生活方式以网络为主题不断进化 北京医疗企业固定资产管理就是其中的一部分 现在 无论是大型的还是小型的网站 都随处可见 不知不觉中已经成为我们生活中不可或缺的存在 随着社会的发展 除了对系统的需求外 我们还要促进经济发展 提高工
  • 【gee】下载modis土地利用类型

    var china ee FeatureCollection projects assets china boundary var lc dataset ee ImageCollection MODIS 061 MCD12Q1 filter
  • Vue3 和Vue2的区别,以及钩子函数的使用

    Vue js 3 和 Vue js 2 是两个主要版本的流行前端框架 它们之间有很多区别 包括性能优化 新特性和改进的API等 以下是一些Vue 3与Vue 2之间的主要区别 以及一些示例代码来说明这些差异 1 性能优化 响应式系统 Vue
  • 新手也能看懂的【前端自动化测试入门】!

    前言 最近在网上搜索前端自动化测试相关的文档 但是发现网上的文章都是偏使用 没有把一些基础概念说清楚 导致后续一口气遇到一些 karma Jasmine jest Mocha Chai BDD 等词汇的时候很容易一头雾水 这次一方面整理一下
  • Vue 如何使用WebSocket与服务器建立链接 持续保持通信

    WebSocket 浏览器通过JavaScript向服务器发出建立WebSocket链接的请求 链接建立后 客户端和服务器端就可以通过TCP链接直接交互数据 WebSocket链接后可以通过 send 方法来向服务器发送数据 并通过 onn
  • 30天精通Nodejs--第十九天:express-文件上传下载

    目录 前言 环境准备与依赖安装 文件上传功能实现 引入并配置express fileupload中间件 注意事项 文件下载功能实现 结语 前言 文件的上传和下载是许多应用程序必备的功能 Node js的Express框架同样可以通过集
  • 考虑光伏出力利用率的电动汽车充电站能量调度策略研究(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 数据
  • 每日变更的最佳实践

    在优维公司内部 我们采用发布单的方式进行每天的应用变更管理 这里给各位介绍优维的最佳实践 变更是需要多角色合作的 而且他是整体研发流程的一部分 在优维内部 我们坚持每日变更 打通开发环节到最终发布上线的全过程 在保证质量的前提下 尽可能提升
  • 【js学习之路】遍历数组api之 `filter `和 `map`的区别

    一 前言 数组是我们在项目中经常使用的数据类型 今天我们主要简述作用于遍历数组的api filter 和 map 的区别 二 filter和map的共同点 首先 我们主要阐述一下 filter 和 map 的共同点 api的参数都是回调函数

随机推荐