Before run-time applets like Remax and TarO3, the main popular solution was to compile vue or React code to WXML in advance, which was limited by the applets’ syntax. More flexible frameworks like React can also be a nightmare for framework authors when it comes to syntactic transformations.
Of course, this is also the framework developers have no choice, the main reason is that the running environment of the applet is very different from the traditional browser environment. For the sake of performance and security, wechat applet uses two threads to do logic layer and rendering layer respectively. The logic layer contains jsCore and can execute JS code, while the render layer is responsible for rendering specific UI, but cannot execute JS directly. The two threads communicate with each other through setData. So in addition to using setData, we can’t directly manipulate the dom structure of the applet through JS, which makes it impossible to directly use frameworks like React/Vue.
Is there a way to run react/ Vue and develop applets without syntax constraints? The answer is definitely there. Kbone/Remax/TarO3 and others have verified the feasibility of this scheme. There are two main approaches:
- With the react-Reconciler, you can customize the renderer’s features to connect the applet’s rendering
- Simulate BOM/DOM API in web environment
The first is the scheme used by Remax, the second is the scheme used by TarO3 and Kbone, and today we will focus on the former.
react-reconciler
The first thing we need to understand is what the React, react-Reconciler and react-DOM are doing. Those of you who have used the React framework know that the JSX code we wrote in the file will eventually be converted by Babel to the form of react. CreateElement, which creates a React element. Alternatively, we can extend a class from react.component. this becomes a React component. So, with the React package we can:
- Create the react element
- Create react Component instance (instance)
But how does light render with these elements and instances that describe the structure of the page?
ReactDOM
We usually render the virtual DOM by writing reactdom.render (Element, container, callback). The role played by the React-Reconciler is illustrated below:
- The Reconciler’s responsibility is to maintain the virtual DOM tree, and the Diff/Fiber algorithm is implemented internally to decide when and what to update.
- The React-DOM defines the specific environment apis to use, such as the browser appendChild, removeChild, and so on. It is then defined into a specific hostConfig that is passed to the Reconciler.
In short, the Reconfiler is the commander who gives the instructions, and the React-DOM is responsible for how the instructions are carried out.
Implement the renderer of the applet layer
With that in mind, we just need to implement a renderer for the applets environment. Of course, the premise is to fully understand the technical details of the custom renderer, you can refer to this official document to learn:
Zh-hans.reactjs.org/docs/implem…
vnode
Remax uses vNode to describe the DOM information inside the applet in the following format:
interface VNode {
type: string;
props: object;
text: string;
children: VNode[];
appendChild(node: VNode): void;
removeChild(node: VNode): void;
insertBefore(newNode: VNode, referenceNode: VNode): void;
}
Copy the code
When rendering for the first time, the entire vNode information will be passed to the rendering layer through the setData interface, and then the entire view will be rendered using the Template syntax provided by wechat applet.
Data update
When the page is updated, it would be very inefficient to re-pass the entire VNode tree through setData for every little change. Here remax’s solution is to pass only the updated part when the update occurs:
applyUpdate() {
const action = {
type: 'splice'.payload: this.updateQueue.map(update= > ({
path: stringPath(update.path),
start: update.start,
deleteCount: update.deleteCount,
item: update.items[0],})),};// Notify the renderer with setData
this.context.setData({ action });
this.updateQueue = [];
}
Copy the code
The entire VNode tree is maintained in the render layer. When updates come in, the render layer simply synchronizes the changes to the entire tree and triggers the template rendering again. Here is a knowledge point, in wechat small program, the code that can be executed by the rendering layer is called WXS, and the equal sign between js and the rendering layer is not completely drawn. WXS cannot directly manipulate data from the logical layer, but can listen for data sent from the logical layer. More details can be found in the official wechat document.
Template recursion
We know that the template syntax of wechat applet does not support recursion, so how to render our vnode tree?
The solution here is to build a certain level of template ahead of time, for example remax defaults to render 20 layers. Exceeding this limit can have a significant impact on performance. Taro3’s approach is to use custom components for recursion if the hierarchy exceeds the limit, because custom components are recursion-enabled. Here’s a simple example to illustrate:
const vnodes = {
type: "text".value: "depth 1 value".child: {
type: "text".value: "depth 2 value".child: {
type: "text".value: "depch 3 value",}}};Copy the code
<template name="TPL_BASE">
<template is="TPL_1" data="{{d: d, i: 1}}" />
</template>
<wxs module="h">
module.exports = {
get: function(i){
return i + 1
}
}
</wxs>
<template name="TPL_1">
<view wx:if="d && d.value"><text>{{d.value}}</text></view>
<template is="{{'TPL_' + h.get(i)}}" data="{{d:d.child, i: h.get(i)}}" />
</template>
<template name="TPL_2">
<view wx:if="d && d.value"><text>{{d.value}}</text></view>
<template is="{{'TPL_' + h.get(i)}}" data="{{d:d.child, i: h.get(i)}}" />
</template>
<template name="TPL_3">
<view wx:if="d && d.value"><text>{{d.value}}</text></view>
<template is="{{'TPL_' + h.get(i)}}" data="{{d:d.child, i: h.get(i)}}" />
</template>
Copy the code
The example here is only rendering three layers, of course the same is true for multiple layers. This part of the work is already done in the construction phase.