Maison >interface Web >Questions et réponses frontales >Les petits programmes peuvent-ils réagir ?
Le mini programme peut utiliser React. Comment l'utiliser : 1. Implémentez un moteur de rendu basé sur "react-reconciler" et générez un DSL ; 2. Créez un mini composant de programme pour analyser et restituer le DSL 3. Installez npm et exécutez Build ; npm dans les outils de développement ; 4. Introduisez le package dans votre propre page, puis utilisez l'API pour terminer le développement.
L'environnement d'exploitation de ce tutoriel : système Windows 10, React version 18.0.0, ordinateur Dell G3.
Les mini-programmes peuvent-ils utiliser React ?
Oui.
Exécuter les composants React directement dans les mini-programmes WeChat
Lorsque j'étudie le développement cross-end, l'un de mes objectifs importants est de permettre aux composants React de s'exécuter dans les mini-programmes WeChat. Au cours de ce processus, j'ai exploré l'architecture des mini-programmes WeChat et suscité de nombreuses réflexions. Quant au développement cross-end, il est en fait difficile d'écrire une seule fois et de l'exécuter n'importe où car les capacités fournies par chaque plate-forme sont différentes. Par exemple, l'applet WeChat fournit des fonctionnalités natives, telles que l'allumage de la caméra ou d'autres fonctions qui nécessitent une fonctionnalité native. Environnement de prise en charge des capacités, bien que le développement des mini-programmes WeChat soit également effectué en vue Web, cela nécessite une certaine réflexion native. Par conséquent, il doit y avoir certaines restrictions pour réaliser l'écriture une fois. Ces restrictions sont destinées à nous empêcher d'utiliser pleinement les capacités du mini-programme et d'utiliser uniquement certaines capacités de mise en page. Par conséquent, je conseille à chacun d’être préparé mentalement lors du développement inter-terminal. Mais si je m'éloigne du développement cross-end et que je ne développe désormais que de petits programmes, puis-je utiliser le réactif que je connais pour le développer ? Puis-je également utiliser le framework nautil que j'ai développé pour le développement ? La réponse est oui, cet article vous guidera étape par étape pour réaliser votre propre développement d'applet React et vous aidera à atteindre l'objectif de migration des projets React vers l'applet dans certains scénarios spécifiques.
Comparaison des solutions pour exécuter React dans des mini-programmes
Actuellement, l'industrie peut mieux prendre en charge les mini-programmes (sauf indication contraire, les mini-programmes font spécifiquement référence aux mini-programmes WeChat) pour exécuter des composants React. Il existe 3 ensembles de solutions, à savoir JD. .com Taro du laboratoire, remax d'une équipe d'Ant Financial (aucun nom d'équipe spécifique n'a été trouvé) et kbone d'une équipe de WeChat.
Taro
Compilée, la nouvelle version est également basée sur le runtime
analysé en wxml+js
Ancienne marque, développement continu, support complet de la plateforme, itération continue
Remax
Runtime, avec des macros compilées
Basé sur un réconciliateur
La mise à jour incrémentielle la plus élégante
pas assez mature, le développement ultérieur est inconnu
Kbone
Exécuter temps, dépendant du webpack
Réalisez-le vous-même Un ensemble d'API DOM
compatible avec vue, et même n'importe quel framework basé sur le rendu DOM
Problèmes de performances (inspection complète), mise à jour presque arrêtée
Les trois ensembles de solutions sont différents, et dans leurs propres idées, tous sont uniques. Personnellement, si vous n'envisagez pas le développement cross-end, il est très utile d'implémenter vous-même un ensemble d'API DOM, car l'interface DOM est une norme HTML et vous n'avez pas besoin d'inventer un ensemble de normes vous-même. et une fois l'API DOM implémentée, toutes les autres applications basées sur DOM peuvent théoriquement prendre en charge son exécution. Cependant, son inconvénient est que chaque fois que vous changez de plate-forme, vous devez implémenter un ensemble d'API DOM pour cette plate-forme. Ce coût est très élevé, car la norme d'interface DOM est extrêmement volumineuse et il est facile d'avoir des bugs lors de la mise en œuvre. . À mon avis, l'implémentation la plus élégante est l'idée deRemax, qui est un moteur de rendu basé sur React-Reconciler. Ce moteur de rendu résume l'instance du composant React dans un DSL unifié, analyse et restitue ce DSL sur différentes plates-formes. .
Mais après la mise à jour itérative de remax, il a commencé à s'appuyer fortement sur ses propres outils de compilation, ce qui m'a directement amené à renoncer à l'utiliser dans le projet. Parce que pour nos propres projets, nous n'avons peut-être pas besoin de tout cela. Nous utilisons uniquement React pour compléter certaines parties de l'ensemble de notre petit programme (par exemple, certains h5 qui ont été écrits en réaction que nous voulons restituer au petit programme). , nous exécutons toujours d'autres parties dans le projet original). S'il y a une dépendance à son outil de compilation, il faudra migrer l'intégralité du projet vers son outil de compilation. Alors autant utiliser taro, un outil ancien et stable.
Idée globale d'implémentation
Après quelques recherches, j'ai décidé d'adopter l'idée de remax, qui consiste à implémenter un moteur de rendu basé sur React-Reconciler, à générer un DSL, puis à créer un petit composant de programme pour analyser et restituer le DSL. . Après avoir terminé l'implémentation, j'ai intégré toutes ces logiques dans le produit final et publié le produit sous la forme de npm. Pour les petits développeurs de programmes, il leur suffit d'installer npm, d'exécuter la build npm dans les outils de développement, puis d'introduire ce package. dans votre propre page et utilisez l'API pour terminer le développement sans avoir besoin d'utiliser des outils de compilation supplémentaires.
Le plus grand avantage de cette solution est qu'elle ne dépend que faiblement (pas) des outils de compilation, de sorte que notre solution peut être exécutée dans n'importe quel projet sans avoir besoin d'introduire des outils de compilation supplémentaires pour changer de pile d'outils. De plus, étant donné que la partie réconciliateur a été regroupée dans le package npm, il s'agit d'un module qui peut être exécuté indépendamment. Par conséquent, vous pouvez même utiliser ce package npm pour restituer des composants de réaction dans des projets de style Vue ou de mini-programme de style natif. comme mpvue.
L'idée d'exécuter des composants React dans l'applet WeChat
Comme le montre l'image ci-dessus, nous passons un composant React via le moteur de rendu basé sur React-Reconciler et créons un pur objet DSL (y compris la fonction de rappel) .Nous Dans le fichier js de la page, cet objet est envoyé au thread de rendu via this.setData, et un composant imbriqué auto-référentiel que nous fournissons est utilisé dans wxml pour restituer le DSL. Une chose à noter ici est que React-Reconciler déclenchera le hook correspondant lorsque le composant sera mis à jour. À ce moment, un nouveau DSL sera à nouveau généré et le rendu sera à nouveau envoyé via this.setData. Par conséquent, le résultat de ce moteur de rendu est différent de celui de la simple utilisation de createElement. Le moteur de rendu prend en charge les fonctions intégrées de réaction telles que les hooks.
Ensuite, j'expliquerai les détails spécifiques afin que vous puissiez autant que possible écrire à la main le code décrit dans cet article, afin que vous puissiez obtenir le même effet que cet article dans votre propre projet. Vous pouvez cloner cet entrepôt localement, voir l'effet en cours et étudier l'ensemble de son processus de mise en œuvre.
Rendu les composants de réaction en objets JS purs
Le moteur de rendu de réaction est essentiellement un exécuteur d'effets secondaires basé sur le système de planification de réaction. Le résultat de l'effet secondaire est une opération DOM dans l'environnement Web, et il est appelé rendu dans le. environnement natif. Le moteur rastérise les graphiques. Dans l'environnement artistique, il appelle la carte son pour jouer le son cette fois, nous avons besoin que le moteur de rendu génère un objet js pur afin qu'il puisse être transmis à l'applet en tant qu'objet. fonction entre les deux threads de l'applet. Le corps du message est transmis et l'interface est rendue dans l'applet en fonction de cet objet.
Certains étudiants m'ont demandé : après la compilation jsx, le résultat de l'exécution de React.createElement n'est-il pas un pur objet JS ? Ici, vous devez comprendre l'essence de réagir. Les composants de React fournissent en fait un système de description pour React, qui décrit la structure des objets spécifiques exprimés par React. Cependant, cette description est abstraite et n’a de sens que lorsque vous l’instanciez et l’exécutez. La description que nous faisons dans le composant ne concerne pas seulement la partie jsx, elle inclut également la logique au niveau métier et programme. Par exemple, dans de nombreux scénarios, nous devons décider quelle partie de jsx renvoyer en fonction de l'état du composant, afin de restituer différentes interfaces. Cette partie du contenu doit s'appuyer sur un environnement d'exécution, qui est le moteur de rendu React.
Dans le passé, nous ne pouvions que simuler React-Dom et écrire nous-mêmes un moteur de rendu selon sa logique de fonctionnement. Désormais, React a créé une bibliothèque spéciale pour son planificateur, React-Reconciler, afin d'aider les développeurs à accéder rapidement au système de planification de React afin qu'ils puissent créer leurs propres moteurs de rendu. Voici une vidéo (apportez votre propre échelle) qui présente l'utilisation de base et les effets de 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) }) }
Dans le code ci-dessus, le contenu spécifique de HostConfig qui n'est pas donné est la clé. Il est utilisé pour préparer un réconciliateur. Du point de vue du code, il s'agit d'une collection de fonctions hook à l'intérieur. chaque fonction hook. Lors de l'utilisation du conteneur, vous pouvez voir qu'à différents moments, les éléments créés, montés et mis à jour que nous avons transmis seront appelés, et ils recevront le conteneur exploité, nous permettant d'obtenir l'objet js (il y en a aussi). fonctions sur le conteneur, mais nous pouvons l'ignorer, car this.setData effacera automatiquement ces fonctions).
Étant donné que ce contenu de configuration est trop compliqué, il faudrait beaucoup d'espace pour l'expliquer clairement, je colle donc directement l'adresse du code source ici. Vous pouvez comprendre de quels éléments de configuration il dispose en lisant le code source, et vous pouvez mettre. this Après avoir divisé une partie du code, exécutez votre propre composant et utilisez console.log pour observer le moment et l'ordre dans lesquels ils sont appelés.
En bref, ces interfaces sont au niveau des connaissances et non d'une logique complexe. Après avoir compris le rôle et le timing d'exécution de chaque élément de configuration, vous pouvez écrire votre propre moteur de rendu. En théorie, ce n'est pas si difficile.
Sur la base de React-Reconciler, j'ai effectué quelques opérations d'effets secondaires dans tous les aspects du temps d'exécution de React. L'essence de ces effets secondaires est de modifier un objet js pur lorsque React est exécuté, il passera par un cycle de vie. Ceci Dans l'une de mes vidéos, j'ai parlé du processus spécifique du cycle de vie de React. Vous pouvez également suivre mon compte public personnel WeChat wwwtangshuangnet et discuter de questions connexes avec moi. Sur chaque nœud du cycle de vie, le planificateur effectuera un effet secondaire, c'est-à-dire modifier l'objet js pur que j'ai fourni.
Je propose deux méthodes pour obtenir l'objet js généré dans le moteur de rendu du mini programme. Après avoir obtenu cet objet js, vous pouvez appeler this.setData de l'applet pour envoyer cet objet au thread de rendu pour le rendu.
利用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视频教程》
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!