VPS参考测评推荐
专注分享VPS主机优惠信息
衡天云优惠活动
荫云优惠活动
wexlayer优惠活动
最新

如何在React中构建小程序? 分享两个实现方案(React创作项目)

主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情!
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作

如何使用React构建小程序?在下一篇文章中,我将通过1500行代码揭示React如何在小程序平台上运行,并学习如何使用React构建小程序。用于建筑。 希望对您有帮助!

您使用过Taro或Remax等框架吗?想知道如何在小程序平台上运行React代码吗?如果是这样,您可能想喝杯咖啡并继续阅读。 本文使用了两种实现React运行在小程序平台上的方案。 如果你现在想阅读这1500行实现代码,你可以直接点击项目的源代码来获取(你可能还需要多喝几杯咖啡)。

项目描述

为了更清楚地说明实施过程,我们将实施计划视为一个项目。 项目需求:在微信小程序平台上运行如下计数器函数的React代码。

导入 React, { Component } from 'react'import { View, Text, Button } from '@leo/components'import './index.css' 导出默认类 Index extends Component {constructor() { super( ) this.state = { count: 0 } this.onAdd Click = this.onAddClick.bind(this) this.onReduceClick = this.onReduceClick.bind(this) }componentDidMount () { console.log(Run 'componentDidMount'') this.setState({ count: 1 }) } onAddClick() { this.setState({ count: this.state.count + 1 }) } onReduceClick() { this.setState({ count: this.state.count - 1 }) } render () { const text = this.state.count % 2 === 0 ? "Even" : "Odd" 返回值 ( t> 上面的代码是此类框架的 React DSL。 要查看实现这个需求的效果,点击这个项目的源码,按照提示运行该项目,现在这个项目的需求和最终结果就是了。明确了,下一步就是明确从需求到结果的过程

重点关注小程序。 开发过小程序搭建框架产品的同学都知道,小程序框架包含了body和pages,body由三个文件组成,位于根目录下,这三个文件必须在:app。 .js(必填,小程序逻辑),app.json(必填,小程序公共配置)。app.wxss(可选,小程序发布样式表)。 所以,要将React代码构建成小程序代码,首先需要生成app.js和app.json文件。 此转换不涉及app.js文件,因此可以直接将app.js内容替换为硬编码的App({})。 app.json 是一个配置文件。 您可以将app.config.js直接添加到您的React项目中以填充您的配置内容,即React。 代码工程目录为:

§ ─ s —— app.config.js // 小程序配置文件,用于生成app.json内容 │ ‐ ─ 页面 │ ‐ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ──────────────────────────────────────── ──────────────────────我 索引 │ . 程序全局配置内容:

module.exports = {pages:['pages/index/index'],window:{NavigationBarTitleTex t:'react-wxapp',navigationBarBackgroundColor:'#282c34'}} ;

使用该配置文件,可以生成app.js和app.json文件,如下:

/*outputDir 生成小程序代码的目录*/fs.writeFileSync(path.join(outputDir, './app.js'), `App({})`)fs.writeFileSync(path .join ( outputDir, './app.json'), JSON.stringify(config,unknown, 2)) //confg 是 app.config.js 文件的内容。

一个小程序页面由四种类型的文件组成:js(必填,页面逻辑)、wxml(必填,页面结构)、json(非必填,页面配置)。 )、wxss(可选,页面样式表)。 在将React代码转换为小程序时,主要考虑的是如何将React代码转换为与程序兼容的js或wxml格式文件,这个我们稍后会详细讨论。

小程序平台运行React方案分析

小程序平台上React代码的实现方式主要有两种。 一种是编译时实现,另一种是运行时实现。 如果你已经看过了,点击这里查看项目源码,可以看到源码中也体现了这两个方法(编译时实现目录:packages/compile-core,运行时实现目录:package/runtime -core)。

与Taro 1.0、2.0一样,编译时的方式主要是采用静态编译的方式将JSX转换为兼容小程序的模板并实现渲染。 该方法的性能接近原生小程序,但存在明显的语法限制。 。 这是因为运行时实现通过react-reconciler重新定义了小程序平台上的React渲染器,让React代码真正在小程序中运行,类似于Taro3.0、Remax等,方法中没有任何内容。 有语法限制,但会降低性能。 本项目源码参考了Taro、Remax等框架源码实现,简化了很多细节。 因此,本项目源码仅适合学习目的,不能用于实际业务。

下面介绍如何实现React在小程序平台上从编译期到运行期运行。

编译时实现

在解释具体实现过程之前,我们首先需要了解编译时实现的概念。 首先,这里的编译并不是传统的高级“编译”。 传统意义上的编译一般指高级语言。 该语言被编译为较低级别的语言,但这里我们只是转换同级别的语言。 也就是说,它将一个 JavaScript 代码字符串编译为另一个 JavaScript 代码字符串。 这里编译类似于“翻译”。 其次,虽然这里称为编译时实现,但并不是所有的实现过程都是编译的,一小部分实现仍然需要运行时协调。 因此,这种方法更适合称为“重编译、轻执行方法”。 同样,运行时实现还包括少量的编译时实现,也称为高执行、低编译方式。

为了更轻松地将一个 JavaScript 代码字符串编译为另一个,我们将直接使用 Babel 工具。 由于篇幅限制,我们在这里不详细介绍如何使用 Babel。 如果你不熟悉 Babel,请阅读这篇文章来熟悉它(是的,这是自我推销)。 接下来我们来分析一下编译时的实现步骤。

1. 将 JSX 转换为与小程序对应的模板。

React 通过 JSX 渲染视图,而小程序通过 wxml 渲染视图来渲染视图。 如果你想在小程序中实现,你应该使用React。 了解如何将JSX转换为对应的小程序wxml。 翻译规则是将JSX中使用的语法转换为小程序中相同功能的语法。 示例:

标签元素翻译:视图、文本,但ton等标签直接映射。 小程序本身的基本组件(改为小写)

样式类名转换:className改为class

==>

事件改造:例如将onClick改为bindtap。

==>

循环转换:将语法更改映射到 wx:for

list。 .map(i => {i}) => {{item}}

语法转换的作用远不止上述类型。 如果你想保证开发者可以使用各种JSX,你应该尽量使用该语法来开发小程序。彻底枚举所有语法转换规则。 如果不是,很可能是开发者使用了不支持转换的写入方式。 事实上,有些编写方法(例如动态生成 JSX 片段)根本无法支持转换。 这就是为什么上述编译时实现方案的缺点是语法有限。 开发人员不能自由编码,必须限制自己开发框架本身。 规则。

由于上述JSX代码语法需要改造比较简单,只涉及到一些简单的语法规则的改造,所以这里可以看到改造后的wxml结果如下: 直接贴出来。 相应的实现代码可以在packages/compile- core/transform/parseTemplate.ts中找到。

计数:{{count}} {{text}} +1

2.运行时适配

如前所述,这个解决方案我称为编译时实现。使用 React 代码作为小程序。 为了在平台上运行,必须在运行时进行适配处理。 自适应处理主要是利用小程序JS逻辑来实现的,其内容主要包括数据渲染、事件处理、生命周期映射等。

小程序的JS逻辑通过构成声明周期、事件等的对象参数进行注册,并通过setData方法触发视图的渲染。

Component({ data: {}, onReady) () { this.setData(..) }, handleClick () {}})

可以通过类配置计数器 React 代码,如下所示:组件逻辑。

类 CustomComponent 扩展s Component { state = { }componentDidMount() { this.setState(..) } handleClick () { }}

从上面两段代码可以看出,小程序通过对象来声明自己的逻辑,而 React 则创建逻辑语句通过课程。 另外,小程序通过setData触发视图(wxml)渲染,React通过setState触发视图(render方法)渲染。 因此,为了能够在小程序平台上运行React逻辑,可以添加运行时shim,以支持两种通过shim编写逻辑的方式。 在介绍runtime shim的具体实现之前,我们需要对上面的React计数器代码进行一个简单的转换过程。 处理后的代码如下:

import React, { Component } from "../../npm/app .js"; // 1.app.js 是一个 shim 实现文件。 export default class Index extends Component { static $$events = ["onAddClick", "onReduceClick"]; // 2. 收集 JSX 事件名称 constructor() { this.state = { count: 0 }; onAddClick = this.onAddClick.bind(this); this.onReduceClick = this.onReduceClick.bind(this); console.log('运行 componentDidMount'); this.setState({ countt: 1 }); onAddClick() { this.setState({ count: this.state.count + 1 }); onReduceClick() { this.setState({ count: this.state.count - 1 } ); } createData() { // 3. 将渲染函数更改为 createData 并将其删除。 __state = argument[0]; // 原始 JSX 代码,返回更新后的状态 // 提供给小程序用于 setData const text = this.state.count % 2 === 0 ? 'Even' : 'odd'; this.__state, { text: text }); 返回 this.__state。 }} 页面(require('../../npm/app.js').createPage(Index)). // 4. 使用运行时 shim 提供的 createPage // 方法进行初始化。 // 复制方法代码进行初始化。

如上面代码所示,有四个位置需要处理。

组件重写,重写逻辑是在运行时shim文件中实现的,即app.js。 具体实现逻辑稍后贴出。

收集原始JSX点击事件对应的回调方法名,以便shim在运行时向小程序平台注册该事件。

原来的render方法中的JSX片段已经转换为wxml了,所以这里的render方法可以去掉JSX片段。 另外,每次运行 setState 时,React 都会触发 render 方法,而 render 方法会接收最新的状态数据来更新视图,因此这里生成的最新状态正是您需要提供给小程序的 setData 方法。 ,从而触发小程序的数据。 在渲染中,为此我们将渲染名称更改为createData(生产小程序的数据data),同时重写内部逻辑以返回最新生成的状态。

使用运行时shim提供的createPage方法进行初始化(createPage方法的具体逻辑稍后贴出),并通过小程序平台提供的Page方法进行注册。 您可以在此处了解 createPage 方法。 返回的数据必须是object类型。

buffer(app.js)的实现如下。

Export class component { // Weight 编写权重组件的实现逻辑 constructor() { this.state = {} } setState(state) { // SetState 最终触发小程序的 SetData Update (this.$Scope. $Component, state)} _init(scope) {this.$scope=scope}} 函数更新,state={}) {$Component.state = Object.assign($component.state, state) let data = $component.createData(state) // 运行createData获取最新状态 data['$leoCompReady'] = true $component .state = data $ Component .$scope.setData(data) // 将状态传递给 setData 进行更新} export function createPage(ComponentClass) { // CreatePage 是逻辑 const Implements componentInstance = new ComponentClass() // 实例化传递给 Re Act 的 Class 组件 const initData =componentInstance.state const option = { Use Use Use Use Use 1 from component - t, lass() this. 向 $component._init(this) 发出退出并使用 outseroutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutoutupdate(this.$component, this.$component.state) }, onReady( ) { if (typeof this.$component.componentDidMount === 'function') { this .$component.componentDidMount()                                                                。                                                                                                                      入口。 //获取React组件中所有事件回调方法的名称 if (events) { events.forEach(eventHandlerName => { ion[eventHandlerName]) return option[eventHandlerName] = function () { this.$component [eventHandlerName] . call(this.$Component)}})} 返回选项}

如上所述,重写了组件类和createPage方法。

组件内声明的状态执行更新方法。 update方法主要将React生成的新状态与旧状态进行合并,并通过上面提到的createData方法获取最新的合并状态。 ,通过将最新状态传递给setData小程序,实现小程序的数据绘制。

createPage方法中的逻辑首先实例化React组件,然后构造小程序逻辑中对应的字面量,并将React组件实例相关的方法绑定到小程序逻辑对象中的字面量上。就是这样。 接下来进行生命周期绑定:在小程序的onReady周期中,启动React组件对应的ComponentDidMount生命周期。 最好进行事件绑定。 通过上面的回调事件名拿React,用小程序逻辑中对应的字面量注册组件实例中对应的事件。金额内,这就完成了小程序平台事件的绑定。 最后,这个对象文字被上面提到的 Page 方法返回以进行注册。

至此,你的React代码就可以在小程序平台上运行了。 您可以通过在项目源代码上运行 npm run build:compile 来查看效果。 编译时实现方案主要将JSX代码的静态编译与运行时shims结合起来,完成在小程序平台上运行的React代码。 这种方案基本上没有性能损失,并且可以在运行时 shim 内完成一些优化(例如去除不必要的渲染数据和减少 setData 数据量),因此其性能降低到与使用原生语法类似。发展。 在某些情况下它可能会更好。 然而,这种解决方案的缺点是语法限制(上面提到过),使其不适合开发,这就催生了运行时实现方案。

运行时实现

如上所示,编译时实现的语法限制主要是由于无法在小程序平台上实际运行React,但是运行时实现解决方案那么。 原理是在小程序平台中实现一个React自定义渲染器来渲染React代码。 这里我们将讲解如何实现remax框架。 本项目源码中的运行时实现也是参考remax框架实现的。

如果您使用 React 开发 Web,您的入口文件将包含类似于以下内容的代码:

import React from 'react'import ReactDom from 'react-dom'import App from './ App'ReactDom.render(App, document.getElementById('root'))

渲染网页事实证明,您需要引用一个名为 . React-dom模块,这个模块的功能是什么?React-dom是一个Web平台的渲染器,主要负责React运行后将Vitrual DOM数据渲染到Web平台。 同样,React 必须原生渲染另外,还有一个针对原生平台的渲染器,React Native。 React 通过在每个平台上实现一个 React 渲染器来实现多平台模式,如下图所示。

如果你想在小程序平台上运行React,只需为你的小程序开发一个自定义渲染器即可。 React官方提供了react-reconciler包来实现自定义渲染器。 官方提供了一个简单的demo来重写react-dom。

使用react-reconciler实现渲染器有两个主要步骤。 第一步是实现类似于ReactDOM.render方法的渲染函数(render方法)。

import ReactReconciler from 'react[k3 ]reconciler'import hostC onfig from './hostConfig' // 主机配置 // 创建一个 Reconciler 实例并将 HostConfig 传递给 Reconcilerconst ReactReconcilerInst =. ReactReconciler(hostConfig)/** * 提供类似于ReactDom.render方法的渲染方法。 * 与ReactDOM相同,有3个参数。 * render(,container, () => console.log( 'rendered')) */export functi on render(element,container,callback) { // 创建根容器 if (!container . _rootContainer) { 容器._rootContainer = ReactReconcilerInst.createContainer(container, false); } //更新根容器 return ReactReconcilerInst.updateContainer(element,container._rootContainer, null, callback);}

如上图所示,第二步,Import hostConfig from " ./主机配置”。 主机配置(HostConfig)是通过协调器-reconciler实现的。 HostConfig是托管环境提供的一组适配器解决方案和配置项,定义了如何创建节点实例、构建节点树、提交和更新操作等。 单击 即可查看完整列表。 需要注意的是,小程序平台不提供DOM API操作,只能通过setData将数据传递到视图层。 因此,Remax重新定义了VNode类型的节点,使得React在调整过程中不会直接修改DOM,而是先更新VNode。 hostConfig文件的内容大致如下:

接口 VNode { id:number; // 节点 ID。 这是一个自动递增的唯一 ID,用于标识节点。 Container: Container; // 类似 ReactDOM.render(, document.getElementById('root')): VNode[]; // 子节点 ; //类型节点,即视图和文本属性等基本组件?:any; // 节点属性:VNode | null;Ring; // 文本节点上的文本appendChild(node: VNode): void; inse rtBefore(newNode: VNode, referenceNode: VNode): void ...} // 主机设置hostConfig = { ... // re Conciler 提交后运行,触发容器进行数据更新(实际上是触发小程序中的setData)。 .applyUpdate(); }, // 创建宿主组件实例并初始化 VNode 节点 createInstance(type, newProps,container) { const id = generated e(); const node = new VNode({ ... } ); }, // 插入节点appendChild(parent, child) {parent.appendChild(child) }, // insertBefore(parent, child, beforeChild) {parent.insert Before(child, beforeChild); }, // 删除节点removeChild(parent, child) {parent.removeChild(child) } ... };

针对上述配置,我们还需要提供一个容器,供小程序setData将VNode数据格式化为JSON数据传递给视图层。 该容器类的实现如下:

class Container {constructor(context) { this.root = new VNode({..}); // 根节点 } toJson(nodes,data) { // 将 VNode 数据格式化为 JSON const json = data || Node. forEach(n ode => { const nodeData = { type:node.type, props:node.props || {} , text:node.text, id:node.id,children:[] } if (node.children) { this.toJson(node.children, nodeData.children) } json.push(nodeData) }) return json } applyUpdate() { // 用于 HostConfig 设置 运行setAfterCommit方法 const root = this.toJson([this.root])[0] console.log(root) this.context.setData({root}); } ...}

然后,封装createPageConfig用于执行渲染的方法。 这里,Page 参数是一个 React 组件,它是上面计数器的组件。

从'react'导入* as React;从'./container'导入容器。 // 上面定义的构造从 tainerimport './render' 渲染。 // 上面定义的渲染方法是export default function createPageConfig(component) { // 该组件是一个React组件 const config = { // 页面方法注册的小程序逻辑对象字面量 data: { root: { Children: [], } }, onLoad() { th his.container = new Container(this, 'root'); const pageElement = React.createElement(component, { page: this, } ); this.element = render(pageElement, this.container); return config;}

至此,我们的小型编程渲染器就基本实现了。 代码要运行,必须通过静态编译React进行修改。 计数器组件实际上是插入的最后一行代码。

导入 React, { Component } from 'react'; 导出默认类 Index extends Component { constructor() { this.state = { this.onAddClick = this.onAddClick.bind ( this); this.onReduceClick = this.onReduceClick.bind(this); } ...} // app.js 封装了上面的 createPage 方法 Page(require('../../npm/app.js').createPage(Index))

里面的代码实际运行一个小程序,但还有另一个过程我在这里没有介绍。 如何将上面Container类的applyUpdate方法生成的页面JSON数据更新到视图中? 首先我们看一下这个 JSON 数据是什么样的:

// 由于篇幅问题,这里只展示了部分数据 {"type": "root" ,"props": {}," id": 0,"children": [{"type": "view","props": {"class": "container"},"id": 12,"children": [{"type": “视图”,“道具”:{“类”:“conut”},“id”:4,“孩子”:[{“类型”:“文本”,“道具”:{},“id”:3 ,"children": [{"type": "plain-text","props": {},"text": "count: ","id": 1,"children": []}, {"type": "plain-text","props": {},"text": "1","id": 2, "children": []}]}]} ... ... }]}

可以看到,JSON 数据实际上是树形 UI 数据,如 Tree 。 要在页外渲染此数据,可以使用渲染小程序提供的模板。 由于小程序模板的递归嵌套存在问题(受微信小程序平台限制),因此需要提供多个相同组件类型的模板。 执行递归渲染。 这是代码:

...

此时,你就可以在小程序中真正运行React代码了。 您可以通过在项目源代码上运行 npm run build:runtime 来查看效果。 运行时解决方案的优点是没有语法限制(如果你不相信我,你可以在这个项目中尝试不同的动态写入方法)。 缺点是性能比较差,主要是setData数据。 性能比编译时方案差,因为体积比较大(前面提到,贴出来的JSON数据明显比编译时方案大)。 当然,业界也针对运行时解决方案做了很多性能优化,比如部分更新、虚拟列表等。 由于篇幅限制,这里就不一一解释了(代码中没有实现)。 。

总结

本文介绍了以最简单的方式使用 React 构建小程序的两种实现选项。 两种选择都有明显的优点和缺点,并且各有其优点。 编译时方案更适合追求良好性能的场景。 对于注重开发体验且对性能要求不高的场景,运行时解决方案应该是您的首选。 如果您想了解更多源码实现,请查看Taro和Remax的官方源码,欢迎大家一起讨论。

项目地址https://github.com/canfoo/react-wxapp

【相关学习推荐:小程序开发教程】

PHP快速学习视频免费教程(入门到精通)

如何学习PHP?如何开始学习PHP?如何快速学习PHP? 别担心,这里很棒提供简单的 PHP 学习教程(从初学者到专家)。 如果需要,您可以保存并下载以供学习。

下载

这几篇文章你可能也喜欢:

本文由主机参考刊发,转载请注明:如何在React中构建小程序? 分享两个实现方案(React创作项目) https://zhujicankao.com/142720.html

【腾讯云】领8888元采购礼包,抢爆款云服务器 每月 9元起,个人开发者加享折上折!
打赏
转载请注明原文链接:主机参考 » 如何在React中构建小程序? 分享两个实现方案(React创作项目)
主机参考仅做资料收集,不对商家任何信息及交易做信用担保,购买前请注意风险,有交易纠纷请自行解决!请查阅:特别声明

评论 抢沙发

评论前必须登录!