热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

解密与思考Rax小程序运行时方案

微信小程序开发栏目介绍Rax小程序运行时的方案。

微信小程序开发教程栏目介绍Rax 小程序运行时的方案。

对于现代的前端框架(React/Vue)来说,底层基本都是通过调用 DOM API 来创建视图。而小程序的视图层模板是需要开发者事先写好的,这意味着动态创建 DOM 的方式在小程序中不被允许。但是,小程序的自定义组件具有的『自引用』特性为动态创建 DOM 打开了突破口。所谓自引用,就是自定义组件支持使用自己作为子节点,也就意味着通过递归引用的方式,我们能够构造任意层级和数量的 DOM 树。

举例来说,假设一个小程序自定义组件 element 的 WXML 模板如下所示:

            {{r.content}}

注意到,element 在模板中递归引用了自身,并通过条件判断终止递归。那么,当逻辑层通过 setData 传递了以下一份数据过来时:

{
  "nodeId": "1",
  "tagName": "view",
  "children": [
    {
      "nodeId": "2",
      "tagName": "text",
      “content”: “我是?"
    },
    {
      "nodeId": "3",
      “tagName": "text",
      "content": "rax"    }
  ]
}

最终呈现出来的视图便成了:


  我是
  rax

通过这种方式,我们巧妙地实现了在 WXML 模板固定的情况下,根据传入的 setData 数据来动态渲染视图的能力。而这,也正是运行时方案能够诞生的基础。

基本原理

Rax 的运行时方案脱胎自 kbone——微信官方推出的小程序与 web 端同构解决方案。kbone 的设计原理可以参考其官网介绍,简单总结就是通过在逻辑层模拟 DOM/BOM API,将这些创建视图的方法转换为维护一棵 VDOM 树,再将其转换成对应 setData 的数据,最后通过预置好的模板递归渲染出实际视图。从 DOM API 到维护 VDOM 树的过程基本原理并不复杂,createElement/appendChild/insertBefore/removeChild 等对应着基本的数据结构的操作。

熟悉 Rax 的同学应该知道,为了支持跨端,Rax 有 driver 的设计。实际上,我们完全可以针对小程序端再编写一个 driver,根据上述原理实现其接口 API 即可。但我们最后的选择还是通过更底层的模拟 BOM/DOM API 来完成了整个渲染机制。这么做的考量是,第一,基于 kbone 开发,这是最快的一套方案,小程序端的 driver 只需复用 web 端的 driver-dom 即可,毕竟底层的 documentwindow 变量都已经模拟好;第二,则是因为我们想为开发者提供更贴近 web 的开发体验。这套方案意味着开发者除了使用 JSX 之外,也是支持直接使用 BOM/DOM API 创建视图的,灵活度会更高一点。我们把目光拉长到整个市面上的小程序运行时框架,remax 通过 react-reconciler 直接从 VDOM 层和小程序对接(类似上面说的 Rax 小程序 driver 设计),而 kbone 和 Taro 3.0 都选择通过模拟 Web 环境来实现渲染。这也与框架开发人员的设计意图有关,见仁见智。Rax 小程序运行时方案的基本原理图如下所示:

MPA or SPA ?

以上架构是逐步演进的结果。最初,我们使用了 webpack 的多 entry 模式打包运行时小程序代码,也就是每个页面都会作为一个 entry 独立打包。这使得从行为上来说小程序更像一个 MPA。这带来的问题就是页面间公共依赖的代码不在同一内存中执行,与原生小程序表现不符。这个差异导致我们最终决定变更工程打包模式。目前版本的 Rax 运行时小程序更符合 SPA 的形式,所有的业务代码都打包到了一个 JS 文件中。

我们将 Rax 工程入口的 rax-app 包在小程序运行时上的链路做了一定改造,其在初始化时会根据路由返回各个页面的 render 函数,该 render 函数创建 root 节点(document.createElement)将对应的 Rax 组件挂载至 其上,并将 root 节点 append 到 body 节点(document.body.appendChild)。小程序每个页面在 onLoad 生命周期中,会创建一个独立的 document 并设置为全局变量,然后调用其对应的 render 函数进行各个页面的独立渲染。

性能调优

从上面的小程序运行时原理来看,其性能相比原生是存在一定差距的,这主要由以下几个方面造成:第一:逻辑层运行完整的 Rax + 通过模拟 DOM/BOM API 处理 VDOM 并生成 setData 数据,需要消耗更多的计算时间;第二,相比原生小程序需要传递更多 setData 数据,如果容器层数据序列化能力较弱,会大大增加数据传输耗时;第三,视图层通过自定义组件递归动态生成视图,而我们知道递归动作本身就是一个性能损耗点。此外,由于无法预先知晓用户需要绑定的属性和事件,自定义组件模板中只能将所有属性和事件预先绑好,这导致小程序运行过程中会触发很多无用的事件,进一步加重负担。经过我们的 benchmark 计算,在支付宝小程序平台上,运行时小程序框架(包括 Rax/Taro/Remax 等)与原生小程序存在约 40% 的性能差距。

Rax 小程序运行时发布后,经测试其性能相比其他运行时框架存在着较为明显的差距,于是我们启动了性能调优的专项计划。通过以下方面的重构,成功将 Rax 小程序运行时小程序的性能拉升至业界领先水平,与 Taro/Remax 基本处于同一水平线。

  • 更新数据精确化。在旧版本中,setData 的数据是全量更新的,虽然有 dom 子树分割批量更新的设计,但是数据传输仍然存在大量冗余。重构版本中,Rax 增加了节点渲染判断,未挂载节点无须触发更新;将所有更新收拢至顶层 root 节点统一批量处理, 并且通过精确计算数据更新的 path,实现局部更新。比如某次更新节点的 class 属性时,setData 的数据可能是:

    {   "root.children.[0].children.[1].class": "active"}
  • 内置小程序组件无需维护其属性列表,而是根据用户传参直接赋值。旧版本中,我们维护了所有内置组件的属性,在获取属性值的时候均需要调用 domNode.getAttribute,具有一定性能开销。重构版本 Rax 直接根据用户传参给属性赋值,并将默认值设置的操作移至视图层 WXS/SJS 中处理。

  • 更新 miniapp-render 中的数据结构。经过梳理,Rax 移除了冗余的 tree 数据,重写了 getaElementById 等 API;重构了 attribute、classList 等类;使用了更符合场景需要的 Map/Set 等数据结构,提升了整体的数据处理性能。

  • 渲染模板优化。在支付宝小程序中,Rax 使用 template 进行递归调用;在微信中,Rax 使用 template 调用 element 再调用 template 的形式以避免微信端递归调用 template 的层数限制。在模板中,我们尽量使用 template is 语法进行判断,减少 a:if/wx:if 条件判断,提升模板递归时的性能。

混合使用

无论是出于旧有业务的迁移,或者是出于性能考虑,Rax 小程序运行时中都存在着混合使用的需求。目前,Rax 已经打通与小程序内置组件、小程序自定义组件、小程序页面、小程序插件混合使用的能力。这其中,使用小程序自定义组件是最为复杂的。

与小程序自定义组件混用

在 Rax 中使用小程序自定义组件,其引入路径需要与 usingComponents 保持一致(例如 import CustomComp from '../components/CustomComp/index')。 在编译阶段,Rax 工程使用 Babel 插件进行代码扫描,检测到 JSX 中使用的某个组件是小程序自定义组件(根据其引入路径是否存在同名 axml 文件)时,会将其使用到的属性和事件进行缓存,然后通过 webpack 插件动态生成至递归模板中。在运行时中的创建节点阶段,通过查询缓存判断节点是否为自定义组件。若是自定义组件,则其渲染数据中会插入缓存中的属性,并且绑定事件至该自定义组件实例。

与编译时组件混用(双引擎混合)

通过 Rax 小程序编译时方案产出的组件,从使用形态上来说,可以直接视为小程序自定义组件。而 Rax 工程加强了运行时与编译时的联系,当在 Rax 小程序运行时中使用编译时组件 npm 包时,用户无需引入组件的具体路径,只需像使用普通组件时一样引入,Rax 工程将自动根据该组件 package.json 中是否存在 miniappConfig 字段来判断其是否为一个 Rax 多端组件,然后直接使用其编译时的组件实现。

未来优化方向

Rax 作为业界唯一一个同时支持编译时和运行时引擎的小程序开发方案,其双引擎混合使用的能力能够较为完美地实现性能与开发效率的平衡。在未来,Rax 将实现更为灵活的双引擎混合使用方式,如支持在单个项目中指定某个组件以编译时引擎编译,为业务提供更高的灵活度。

总结

以上就是 Rax 小程序运行时方案的原理解析。运行时方案解决了编译时方案天生的语法限制问题,但是也存在着明显的性能掣肘。可以说,在当前 2020 年这个节点,小程序开发仍然没有所谓的银弹,也许 Rax 小程序双引擎的融合会是一个相对范围内的最优解。逆标准而上的小程序究竟能走多远,谁也无法下定论,未来相当一段时间内,开发者仍然要面临种种问题。站在小程序开发框架的角度,只希望所有开发者能够选择对自己最合适的框架,爽快而又高效地完成小程序的开发。

相关免费学习推荐:微信小程序开发教程

以上就是解密与思考Rax 小程序运行时方案的详细内容,更多请关注其它相关文章!


推荐阅读
  • 程序员如何优雅应对35岁职业转型?这里有深度解析
    本文探讨了程序员在职业生涯中如何通过不断学习和技能提升,优雅地应对35岁左右的职业转型挑战。我们将深入分析当前热门技术趋势,并提供实用的学习路径。 ... [详细]
  • Java项目分层架构设计与实践
    本文探讨了Java项目中应用分层的最佳实践,不仅介绍了常见的三层架构(Controller、Service、DAO),还深入分析了各层的职责划分及优化建议。通过合理的分层设计,可以提高代码的可维护性、扩展性和团队协作效率。 ... [详细]
  • 本文详细介绍了在企业级项目中如何优化 Webpack 配置,特别是在 React 移动端项目中的最佳实践。涵盖资源压缩、代码分割、构建范围缩小、缓存机制以及性能优化等多个方面。 ... [详细]
  • 微信小程序:授权登录与手机号绑定
    本文详细介绍了微信小程序中用户授权登录及绑定手机号的流程,结合官方指引和实际开发经验,提供了一套完整的实现方案,帮助开发者更好地理解和应用。 ... [详细]
  • 本文档汇总了Python编程的基础与高级面试题目,涵盖语言特性、数据结构、算法以及Web开发等多个方面,旨在帮助开发者全面掌握Python核心知识。 ... [详细]
  • 微信小程序中实现位置获取的全面指南
    本文详细介绍了如何在微信小程序中实现地理位置的获取,包括通过微信官方API和腾讯地图API两种方式。文中不仅涵盖了必要的准备工作,如申请开发者密钥、下载并配置SDK等,还提供了处理用户授权及位置信息获取的具体代码示例。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 本文介绍了如何利用npm脚本和concurrently工具,实现本地开发环境中多个监听服务的同时启动,包括HTTP服务、自动刷新、Sass和ES6支持。 ... [详细]
  • 本文探讨了在通过 API 端点调用时,使用猫鼬(Mongoose)的 findOne 方法总是返回 null 的问题,并提供了详细的解决方案和建议。 ... [详细]
  • 本文将深入探讨PHP编程语言的基本概念,并解释PHP概念股的含义。通过详细解析,帮助读者理解PHP在Web开发和股票市场中的重要性。 ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
  • 基于Node.js、Express、MongoDB和Socket.io的实时聊天应用开发
    本文详细介绍了使用Node.js、Express、MongoDB和Socket.io构建的实时聊天应用程序。涵盖项目结构、技术栈选择及关键依赖项的配置。 ... [详细]
  • 本文详细介绍了如何使用 PHP 接收并处理微信支付的回调结果,确保支付通知能够被正确接收和响应。 ... [详细]
author-avatar
手机用户2502940165
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有