Home > Article > Web Front-end > Can small programs use react?
Mini programs can use react. How to use it: 1. Implement a renderer based on "react-reconciler" and generate a DSL; 2. Create a mini program component to parse and render DSL; 3. Install npm, and execute the build npm in the developer tools; 4. Introduce the package into your own page, and then use the API to complete the development.
The operating environment of this tutorial: Windows 10 system, react18.0.0 version, Dell G3 computer.
Can mini programs use react?
able.
Run React components directly in WeChat mini programs
When studying cross-end development, one of my important goals is to allow react components to run on In the WeChat applet. During this process, I explored the architecture of WeChat mini programs and aroused a lot of thinking. As for cross-end development, it is actually difficult to write once and run anywhere because the capabilities provided by each platform are different. For example, the WeChat applet provides native capabilities, such as turning on the camera or other functions that require a native environment. Supporting capabilities, although development in WeChat mini programs is also carried out in webview, it requires some native thinking. Therefore, there must be some restrictions to achieve write once. These restrictions determine that we cannot fully utilize the capabilities of the mini program and only use some layout capabilities. Therefore, I advise everyone to be mentally prepared when doing cross-terminal development. But if I break away from cross-end development and now I only develop small programs, can I use the react I am familiar with to develop it? Even, can I use the nautil framework I developed for development? The answer is yes, this article will take you step by step to realize your own react applet development, and help you complete the goal of migrating react projects to applet in certain specific scenarios.
Comparison of solutions for running React in mini programs
Currently, the industry can better support mini programs (unless otherwise specified, mini programs specifically refer to WeChat mini programs) to run React components. There are 3 sets of solutions, including taro from JD.com’s Above Lab, remax from a team at Ant Financial (the specific team name was not found), and kbone from a team at WeChat.
Taro
Compiled, the new version is also based on runtime
Parsed into wxml js
Old brand, continuous development, full platform support, continuous iteration
Remax
Runtime, with compiled macros
Based on reconciler
The most elegant, incremental update
Not mature enough, subsequent development is unknown
Kbone
When running, rely on webpack
Implement a set of DOM API by myself
Compatible with vue, even any framework based on DOM rendering
Performance issues (full inspection), almost stopped updating
3 sets of plans are different from each other, and they are all unique in their respective ideas. Personally, if you do not consider cross-end development, it is very valuable to implement a set of DOM API by yourself, because the DOM interface is an HTML standard, and you do not need to invent a set of standards yourself, and once it is implemented DOM API, then all other applications based on DOM can theoretically support running on it. However, its disadvantage is that every time you change a platform, you have to implement a set of DOM API for this platform. This cost is very high, because the DOM interface standard is extremely large and it is easy to have bugs during implementation. In my opinion, the most elegant implementation is Remax's idea, which is to make a renderer based on react-reconciler. This renderer abstracts the react component instance into a unified DSL, and parses and renders this DSL on different platforms. .
But after remax was updated iteratively, it began to rely heavily on its own compilation tools, which directly caused me to give up using it in the project. Because for our own projects, we may not actually need all of it. We only use react to complete certain parts of our entire small program (for example, some h5 that have been written in react we want to render to the small program) program, we still run other parts in the original project). If there is a dependence on its compilation tool, we will have to migrate the entire project to its compilation tool. Then I might as well just use taro, an old and stable tool.
Overall implementation idea
After some research, I decided to adopt the remax idea, which is to implement a renderer based on react-reconciler, generate a DSL, and then Create a small program component to parse and render this DSL. After completing the implementation, I built all these logics into the final product and published the product in the form of npm. For small program developers, they only need to install npm, execute the build npm in the developer tools, and then Introduce this package into your own page and use the API to complete development without the need to use additional compilation tools.
The biggest advantage of this solution is that it has weak (no) dependence on compilation tools, so that our solution can be run in any project without the need to introduce additional compilation tools to switch tool stacks. In addition, because the reconciler part has been packaged into the npm package, it is a module that can be run independently. Therefore, you can even use this npm package to render react components in Vue-style or mini-program native-style projects such as mpvue.
The idea of running react components in WeChat applet
As shown in the figure above, we create a react component through the renderer based on react-reconciler A pure object of DSL (including callback function) is obtained. In the js file of the page, we send this object to the rendering thread through this.setData. In wxml, we use a self-referential nested component we provide to perform DSL render. One thing to note here is that react-reconciler will trigger the corresponding hook when the component is updated. At this time, a new DSL will be generated again and the rendering will be sent through this.setData again. Therefore, the result of this renderer is different from that of simply using createElement. The renderer supports built-in functions of react such as hooks.
Next, I will explain the specific details so that you can hand-write the code described in this article as much as possible, so that you can achieve the same effect as this article in your own project. You can clone this warehouse locally, see the running effect, and study its entire implementation process.
Render react components as pure JS objects
The react renderer is essentially a side effect executor based on the react scheduling system. The results of the side effects are in the web environment It is the operation of DOM. In the native environment, it calls the rendering engine to rasterize the graphics. In the art environment, it calls the sound card to play the sound. In our plan this time, we need the renderer to generate a pure js object to facilitate the handover. The applet is passed as a message body between the two threads of the applet, and the interface is rendered in the applet based on this object.
Some students asked me: After jsx compilation, isn’t the execution result of React.createElement a pure JS object? Here you need to understand the essence of react. The components of react actually provide a description system for react, which describes the structure of the specific objects expressed by react. However, this description is abstract and only makes sense when you instantiate it and run it. The description we make in the component is not just the jsx part, it also includes business and program level logic. For example, in many scenarios, we need to decide which part of jsx to return based on the component status, so as to render different interfaces. This part of the content needs to rely on an environment for execution, which is the react renderer.
In the past, we could only simulate react-dom and write a renderer by ourselves according to its operating logic. Now, React has made a special library for its scheduler, react-reconciler, to help developers quickly access React's scheduling system so that they can build their own renderers. Here is a video (bring your own ladder) that introduces the basic usage and effects of react-reconciler.
import Reconciler from 'react-reconciler' const container = {} const HostConfig = { // ... 极其复杂的一个配置 } const reconcilerInstance = Reconciler(HostConfig) let rootContainerInstance = null export function render(element, { mounted, updated, created }) { if (!rootContainerInstance) { rootContainerInstance = reconcilerInstance.createContainer(container, false, false) } return reconcilerInstance.updateContainer(element, rootContainerInstance, null, () => { notify = { mounted, updated, created } created && created(container) mounted(container.data) }) }
In the above code, the specific content of HostConfig that is not given is the key. It is used to prepare a Reconciler. From a code perspective, it is a collection of hook functions. We need to configure it inside each hook function. Write some side effects to operate the container. You can see that at different times, the created, mounted, and updated we passed in will be called, and they receive the operated container, allowing us to obtain the js object (there are also containers on the container). There are some functions, but we can ignore them because this.setData will automatically clear them).
Because this configuration content is too complicated, it would take a lot of space to explain it clearly, so I directly paste the source code address here. You can understand what configuration items it has by reading the source code, and You can split this part of the code out, run a component of your own, and use console.log to observe the timing and order in which they are called.
In short, these interfaces are at the knowledge level, not complex logic. After understanding the role and execution timing of each configuration item, you can write your own renderer. In theory, it's not that difficult.
Based on react-reconciler, I have done some side effects in every aspect of react running. The essence of these side effects is to modify a pure js object. When react is run, it will go through a Life cycle, this is discussed in one of my videos about the specific process of react's life cycle. You can also follow my personal WeChat public account wwwtangshuangnet and discuss related issues with me. On each life cycle node, the scheduler will perform a side effect, that is, modify the pure js object I provided.
I provide two methods for obtaining the generated js object in the renderer of the mini program. After getting this js object, you can call this.setData of the applet and send this object to the rendering thread for rendering.
利用react渲染器得到的纯对象上存在一些函数,调用这些函数会触发它们对应的逻辑(比如调用setState触发hooks状态更新),从而触发调度器中的钩子函数执行,container对象再次被修改,updated被再次调用,this.setData被再次执行,这样,就实现了真正的react运行时在小程序中的植入。
嵌套递归自引用组件
渲染线程接收到this.setData发送过来的js对象后,如何将这个对象作为布局的信息,渲染到界面上呢?由于小程序的特殊架构,它为了安全起见,渲染线程中无法执行可操作界面的脚本,所有的渲染,都得依靠模板语法和少量的wxs脚本。所以,要怎么做呢?
小程序提供了自定义组件的功能,在app.json或对应的page.json中,通过usingComponents来指定一个路径,从而可以在wxml中使用这个组件。而有趣的地方在于,组件本身也可以在组件自己的component.json中使用usingComponents这个配置,而这个配置的内容,可以直接指向自己,例如,我在自己的组件中,这样自引用:
// dynamic.json { "usingComponents": { "dynamic": "./dynamic" } }
自己引用自己作为组件之后,在其wxml中,我们就可以使用组件自己去渲染子级数据,即一种嵌套递归的形式进行渲染。
我规定了一种特别的数据结构,大致如下:
{ type: 'view', props: { class: 'shadow-component', bindtap: (e) => { ... }, }, children: [ { type: 'view', props: {}, children: [ ... ], }, ], }
模板中,通过对type的判断,选择不同的模板代码进行渲染。
<block wx:if="{{ type === 'view' }}"> <view class="{{ props.class }}" bindtap="bindtap"> <block wx:if="{{ children.length }}" wx:for="{{ children }}"> <dynamic data="{{ item }}" /> <!-- 嵌套递归 --> </block> </view> </block>
在wxml中把所有组件通过这种形式枚举出来之后,这个组件就能按照上述的数据结构递归渲染出整个结构。
当然,这里还需要处理一些细节,例如响应data的变化,事件响应函数等,你可以通过源码了解具体要怎么处理。另外,微信小程序this.setData限制在1M以内,我虽然还没有尝试过很大的数据,但是,这个限制肯定在将来是一个风险点,我现在还没有解决,还在思考应该怎么最小化更新粒度。
不支持直接JSX的变通方法
小程序的编译,没有办法自己配置支持新语法,所以如果我们在小程序代码中使用jsx,就必须先走一遍自己的编译逻辑。有两种解决办法,一种是不使用jsx语法,而是使用hyperscript标记语法,比如:
import { createElement as h } from 'react' function Some() { return h( 'view', { class: 'some-component' }, h( 'view', { class: 'sub-view' }, '一段文字', ), '一段文字', ) }
这样的写法显然没有直接写jsx来的方便,但是阅读上没有什么障碍,且不需要将jsx编译的过程。
另一种办法是走一遍编译,在小程序的页面目录下,创建一个页面同名的.jsx文件,再利用bebel将它编译为.js文件。但是这样的话,你需要在发布小程序的时候,忽略掉所有的.jsx文件。另外,还有一个坑是,小程序的编译不提供process.env,所以编译react的结果用的时候会报错。解决办法是把react的cjs/react.production.min.js作为react的入口文件,通过小程序的构建npm的相关配置逻辑,指定react构建的文件。
结语
本文详细讲解了如何在微信小程序中直接运行react组件的思路,同时,你可以参考这个仓库,运行效果看看,研究它的整个实现过程。总结而言,这个方法分为3个部分:1. 基于react-reconciler实现一个把react组件渲染为纯js对象的渲染器,之所以需要纯js对象,是因为小程序发送到渲染线程的数据必须是纯对象。2. 利用小程序的自定义组件,实现自引用嵌套递归的组件,用于利用上一步得到的js对象渲染成真正的界面。3. 解决jsx问题,将前两步的结果,在page中进行实施,以真正完成在小程序中渲染react组件的效果。当然,本文阐述过程,仅仅提供了这套思路,在真正用到项目中时,使用过程中肯定还会遇到一些坑,仅能作为原有小程序开发项目的补充手段,比如之前写好的react组件不想重新写成小程序版本,那么就可以使用这个方法,同时在渲染组件的地方,把DOM的标签,映射为小程序的标签,就可以在一定程度上解决原有react代码复用的问题。如果你在实操过程中遇到什么问题,欢迎在本文下方留言讨论~
文中链接: Nautil框架:https://github.com/tangshuang/nautil 演示仓库:https://gitee.com/frustigor/wechat-dynamic-component Building a Custom React Rendere: https://www.youtube.com/watch?v=CGpMlWVcHok
推荐学习:《react视频教程》
The above is the detailed content of Can small programs use react?. For more information, please follow other related articles on the PHP Chinese website!