집 >위챗 애플릿 >미니 프로그램 개발 >Rax 애플릿 런타임 솔루션에 대한 복호화 및 생각
2020년 3월, Rax 애플릿은 컴파일 타임 체계를 지원한 후 런타임 체계를 지원하는 버전을 출시했습니다. 현재까지 Rax는 컴파일 타임 및 런타임 솔루션을 모두 지원하는 업계 유일의 소규모 프로그램 개발 프레임워크입니다. 이 기사에서는 Rax 애플릿 런타임 솔루션의 원리와 우리의 생각을 소개합니다.
런타임 솔루션을 소개하기 전에 컴파일 타임 솔루션이 무엇인지 검토해 보겠습니다. 이름에서 알 수 있듯이 컴파일 타임 솔루션은 컴파일에 중점을 두고 있으며 대표적인 프레임워크는 Taro v2.x입니다. 정적 컴파일을 통해 JSX를 미니 프로그램의 템플릿 언어(예: WXML/AXML 등)로 변환한 후 이를 경량 런타임 JS 코드로 보완하여 미니 프로그램의 라이프사이클과 React 라이프사이클 간의 차이를 원활하게 만듭니다. , 제작 사용자는 친숙한 React DSL을 사용하여 작은 프로그램을 개발할 수 있습니다. Rax의 컴파일 타임 체계 원리는 Taro v2.x의 것과 유사합니다. 구현 세부 사항은 이전 기사 Rax to Mini 프로그램 링크의 원리 분석(1) 및 원리 분석을 참조할 수 있습니다. Rax 미니 프로그램 편집 계획. 런타임 솔루션은 컴파일 타임 솔루션과 달리 런타임에 렌더링 기능을 구현하는 데 중점을 두고 정적 컴파일에 의존하지 않으므로 문법적 제한이 거의 없다는 점이 가장 큰 특징입니다. 런타임 솔루션 구현의 원리를 살펴보겠습니다.
미니 프로그램의 기본 구현은 실제로 웹 기술을 기반으로 하지만 개발자 수준으로 보면 웹과는 매우 다릅니다. 미니 프로그램에서는 로직 레이어와 뷰 레이어가 분리되어 있으며, 로직 레이어는 고유한 setData
메소드를 통해 렌더링을 트리거하기 위해 뷰 레이어에 데이터를 전달하고, 뷰 레이어는 이를 통해 로직 레이어 코드를 트리거합니다. 이벤트의 아키텍처는 그림과 같습니다. 웹 개발에 비해 개발자는 JS를 사용하여 브라우저에서 제공하는 DOM/BOM API를 호출하여 원하는 대로 콘텐츠를 조작하고 렌더링할 수 있습니다. 미니 프로그램의 아키텍처는 더 폐쇄적이고 안전하지만 웹 코드를 직접 실행할 수 없다는 의미이기도 합니다. 미니 프로그램에서 setData
方法将数据传递至视图层触发渲染,视图层则通过事件的方式触发逻辑层代码,其架构如下图所示。相比 Web 开发时开发者可以通过 JS 调用浏览器提供的 DOM/BOM API 随心所欲操作渲染内容,小程序的架构更加封闭也更安全,但也意味着 Web 代码无法直接在小程序上运行。
对于现代的前端框架(React/Vue)来说,底层基本都是通过调用 DOM API 来创建视图。而小程序的视图层模板是需要开发者事先写好的,这意味着动态创建 DOM 的方式在小程序中不被允许。但是,小程序的自定义组件具有的『自引用』特性为动态创建 DOM 打开了突破口。所谓自引用,就是自定义组件支持使用自己作为子节点,也就意味着通过递归引用的方式,我们能够构造任意层级和数量的 DOM 树。
举例来说,假设一个小程序自定义组件 element 的 WXML 模板如下所示:
<view> <block> <element></element> </block></view><text> {{r.content}}</text>复制代码
注意到,element 在模板中递归引用了自身,并通过条件判断终止递归。那么,当逻辑层通过 setData
传递了以下一份数据过来时:
{ "nodeId": "1", "tagName": "view", "children": [ { "nodeId": "2", "tagName": "text", “content”: “我是?" }, { "nodeId": "3", “tagName": "text", "content": "rax" } ] }复制代码
最终呈现出来的视图便成了:
<view> <text>我是</text> <text>rax</text></view>复制代码
通过这种方式,我们巧妙地实现了在 WXML 模板固定的情况下,根据传入的 setData
数据来动态渲染视图的能力。而这,也正是运行时方案能够诞生的基础。
Rax 的运行时方案脱胎自 kbone——微信官方推出的小程序与 web 端同构解决方案。kbone 的设计原理可以参考其官网介绍,简单总结就是通过在逻辑层模拟 DOM/BOM API,将这些创建视图的方法转换为维护一棵 VDOM 树,再将其转换成对应 setData
{ "root.children.[0].children.[1].class": "active"}复制代码🎜 요소는 템플릿에서 자신을 재귀적으로 참조하고 조건부 판단을 통해 재귀를 종료합니다. 그런 다음 논리 계층이
setData
를 통해 다음 데이터를 전달하면: 🎜rrreee🎜최종 보기는 다음과 같습니다. 🎜rrreee🎜이런 방식으로 우리는 WXML을 영리하게 구현합니다. 템플릿이 수정되면 동적으로 들어오는 setData
데이터를 기반으로 뷰를 렌더링합니다. 이것이 런타임 솔루션 탄생의 기초입니다. 🎜Rax에 익숙한 학생들은 Rax가 크로스 터미널을 지원하기 위해 드라이버 디자인을 가지고 있다는 것을 알아야 합니다. 실제로 우리는 작은 프로그램을 위한 또 다른 드라이버를 작성하고 위의 원칙을 기반으로 해당 인터페이스 API를 구현할 수 있습니다. 그러나 우리의 최종 선택은 하위 수준의 시뮬레이션된 BOM/DOM API를 통해 전체 렌더링 메커니즘을 완성하는 것이었습니다. 이를 위한 고려 사항은 먼저 가장 빠른 솔루션인 kbone 개발을 기반으로 합니다. 미니 프로그램 측 드라이버는 결국 기본 문서 및 window
변수가 시뮬레이션되었습니다. 둘째, 개발자에게 웹에 더 가까운 개발 경험을 제공하고 싶기 때문입니다. 이 솔루션은 개발자가 JSX를 사용하는 것 외에도 BOM/DOM API를 직접 사용하여 보다 유연하게 뷰를 생성할 수 있음을 의미합니다. 우리는 시장에 있는 모든 미니 프로그램 런타임 프레임워크를 살펴봅니다. Remax는 반응 조정자를 통해 VDOM 계층의 미니 프로그램과 직접 인터페이스하는 반면(위에서 언급한 Rax 미니 프로그램 드라이버 설계와 유사) kbone과 Taro 3.0은 모두 시뮬레이션을 사용하도록 선택합니다. . 렌더링을 구현하는 웹 환경입니다. 이는 프레임워크 개발자의 디자인 의도와도 관련이 있습니다. Rax 애플릿 런타임 솔루션의 기본 도식 다이어그램은 다음과 같습니다. document
和 window
变量都已经模拟好;第二,则是因为我们想为开发者提供更贴近 web 的开发体验。这套方案意味着开发者除了使用 JSX 之外,也是支持直接使用 BOM/DOM API 创建视图的,灵活度会更高一点。我们把目光拉长到整个市面上的小程序运行时框架,remax 通过 react-reconciler 直接从 VDOM 层和小程序对接(类似上面说的 Rax 小程序 driver 设计),而 kbone 和 Taro 3.0 都选择通过模拟 Web 环境来实现渲染。这也与框架开发人员的设计意图有关,见仁见智。Rax 小程序运行时方案的基本原理图如下所示:
Rax 小程序运行时中,模拟 DOM/BOM API 的库为 Rax 애플릿 런타임 솔루션에 대한 복호화 및 생각,其支持的 API 如下:
除了处理渲染数据外,另一个比较重要的事情便是事件系统。其通过 EventTarget
基类实现了一套完整的事件派发机制。逻辑层 DOM 节点均继承自 EventTarget
,通过唯一的 nodeId
来收集自身绑定事件。视图层模板上的每个内置组件都会绑定 nodeId
,并监听所有可触发的事件,比如一个简单的 view 标签,会将 bindtap/bindtouchstart/bindtouchend 等事件都进行绑定。在事件触发时,通过 event.currentTarget.dataset.nodeId
获取到目标节点 id,再触发该节点上用户绑定的对应函数。
Rax 小程序运行时的工程主体流程 follow 了 Rax Web 的设计,Web 端 Webpack 打包出的 JS Bundle 可以在小程序运行时中复用。我们通过插件将 Rax 애플릿 런타임 솔루션에 대한 복호화 및 생각 模拟出的 window 和 document 变量注入该 bundle,再生成一个固定的小程序项目骨架,在 app.js 中加载 JS Bundle 即可。其整体工程结构如下图所示:
以上架构是逐步演进的结果。最初,我们使用了 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
EventTarget
기본 클래스를 통해 완전한 이벤트 전달 메커니즘을 구현합니다. 논리 계층 DOM 노드는 모두 EventTarget
에서 상속되며 고유한 nodeId
를 통해 자체 바인딩 이벤트를 수집합니다. 뷰 레이어 템플릿의 각 내장 구성 요소는 nodeId
에 바인딩되고 트리거 가능한 모든 이벤트를 수신합니다. 예를 들어 간단한 뷰 태그는 bindtap/bindtouchstart/bindtouchend 및 기타 이벤트를 바인딩합니다. 이벤트가 트리거되면 event.currentTarget.dataset.nodeId
를 통해 대상 노드 ID를 얻은 후 해당 노드의 사용자 바인딩 함수가 트리거됩니다. 🎜render
기능이 반환됩니다. render
함수는 루트 노드(document.createElement
)를 생성하고 해당 Rax 구성 요소를 여기에 마운트한 다음 루트 노드를 본문 노드()에 추가합니다. document.body.appendChild
). 미니 프로그램 각 페이지의 onLoad 수명 주기 동안 독립적인 문서
가 생성되어 전역 변수로 설정되고 해당 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 中处理。
更新 Rax 애플릿 런타임 솔루션에 대한 복호화 및 생각 中的数据结构。经过梳理,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 미니 프로그램 듀얼 엔진의 융합은 비교적 넓은 범위 내에서 최적의 솔루션이 될 것입니다. 표준에 어긋나는 미니 프로그램이 어디까지 갈 수 있을지는 아무도 알 수 없습니다. 개발자들은 앞으로도 한동안 다양한 문제에 직면하게 될 것입니다. 소규모 프로그램 개발 프레임워크의 관점에서 볼 때 모든 개발자가 자신에게 가장 적합한 프레임워크를 선택하여 소규모 프로그램 개발을 빠르고 효율적으로 완료할 수 있기를 바랍니다.
관련 무료 학습 권장사항: WeChat 미니 프로그램 개발 튜토리얼
위 내용은 Rax 애플릿 런타임 솔루션에 대한 복호화 및 생각의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!