摘要:下面我们介绍一种全新的处理思路,这种思路在小程序运行期间和真正的几无区别,不会改变任何代码语义,表达式只会被处理为方法调用,实际运行的时候就是普通对象,最终通过其他方式渲染出小程序视图。最终渲染出小程序视图。
React社区一直在探寻使用React语法开发小程序的方式,其中比较著名的项目有Taro,nanachi。而使用React语法开发小程序的难点主要就是在JSX语法上,JSX本质上是JS,相比于小程序静态模版来说太灵活。本文所说的新思路就是在处理JSX语法上的新思路,这是一种更加动态的处理思路,相比于现有方案,基本上不会限制任何JSX的写法,让你以真正的React方式处理小程序,希望这个新思路可以给任何有志于用React开发小程序的人带来启发。
现有思路的局限在介绍新的思路之前,我们先来看下Taro(最新版1.3),nanachi是怎么在小程序端处理JSX语法的。简单来说,主要是通过在编译阶段把JSX转化为等效的小程序wxml来把React代码运行在小程序端的。
举个例子,比如React逻辑表达式:
xx &&Hello
将会被转化为等效的小程序wx:if指令:
Hello
这种方式把对JSX的处理,主要放在了编译阶段,他依赖于编译阶段的信息收集,以上面为例,它必须识别出逻辑表达式,然后做对应的wx:if转换处理。
那编译阶段有什么问题和局限呢?我们以下面的例子说明:
class App extends React.Component { render () { const a =Hello const b = a return ({b} ) } }
首先我们声明 const a =
这个例子不是特别复杂,却报错了。
要想理解上面的代码为什么报错,我们首先要理解编译阶段。本质上来说在编译阶段,代码其实就是‘字符串’,而编译阶段处理方案,就需要从这个‘字符串’中分析出必要的信息(通过AST,正则等方式)然后做对应的等效转换处理。
而对于上面的例子,需要做什么等效处理呢?需要我们在编译阶段分析出b是JSX片段:b = a =
所以在编译阶段 是无法简单确定b的值的。
我们再仔细看下上图的报错信息:a is not defined。
为什么说a未定义呢?这是涉及到另外一个问题,我们知道
// ReactElement对象 { tag: Text, props: null, children: "Hello" ... }
所以上面那一段代码在JS环境真正运行的时候,大概等效如下:
class App extends React.Component { render () { const a = { tag: Text, props: null, children: "Hello" ... } const b = a return { tag: View, props: null, children: b ... } } }
但是,我们刚说了编译阶段需要对JSX做等效处理,需要把JSX转换为wxml,所以
正因为编译时方案,有如上的限制,在使用的时候常常让你有“我还是在写React吗?”这种感觉。
下面我们介绍一种全新的处理思路,这种思路在小程序运行期间和真正的React几无区别,不会改变任何代码语义,JSX表达式只会被处理为React.createElement方法调用,实际运行的时候就是普通js对象,最终通过其他方式渲染出小程序视图。下面我们仔细说明一下这个思路的具体内容。
第一步:给每个独立的JSX片段打上唯一标识uuid,假定我们有如下代码:
const a =Hello const y =
我们给a片段,y片段 添加了uuid属性
第二步:把React代码通过babel转义为小程序可以识别的代码,例如JSX片段用等效的React.createElement替换等
const a = React.createElement(Text, { uuid: "000001" }, "Hello");
第三步:提取每个独立的JSX片段,用小程序template包裹,生成wxml文件
Hello
注意这里每一个template 的name标识和 JSX片段的唯一标识uuid是一样的。最后,需要在结尾生成一个占位模版:。
第四步:修改ReactDOM.render的递归(React 16.x之后,不在是递归的方式)过程,递归执行阶段,聚合JSX片段的uuid属性,生成并返回uiDes数据结构。
第五步:把第四步生成的uiDes,传递给小程序环境,小程序把uiDes 设置给占位模版,渲染出最终的视图。
我们以上面的App组件的例子来说明整个过程,首先js代码会被转义为:
class App extends React.Component { render () { const a = React.createElement(Text, {uuid: "000001"}, "Hello"); const b = a return ( React.createElement(View, {uuid: "000002"} , b); ) } }
同时生成wxml文件:
Hello
使用我们定制之后render执行ReactDOM.render(
const uiDes = { name: "000002", child0001: { name: 000001, ... } ... }
小程序获取到这个uiDes,设置给占位模版。 最终渲染出小程序视图。
在这整个过程中,你的所有JS代码都是运行在React过程中的,语义完全一致,JSX片段也不会被任何特殊处理,只是简单的React.createElement调用,另外由于这里的React过程只是纯js运算,执行是非常迅速的,通常只有几ms。最终会输出一个uiDes数据到小程序,小程序通过这个uiDes渲染出视图。
现在我们在看之前的赋值const b = a,就不会有任何问题了,因为a 不过是普通对象。另外对于常见的编译时方案的限制,比如任意函数返回JSX片段,动态生成JSX片段,for循环使用JSX片段等等,都可以完全解除了,因为JSX片段只是js对象,你可以做任何操作,最终ReactDOM.render会搜集所有执行结果的片段的uuid标识,生成uiDes,而小程序会根据这个uiDes数据结构渲染出最终视图。
可以看出这种新的思路和以前编译时方案还是有很大的区别的,对JSX片段的处理是动态的,你可以在任何地方,任何函数出现任何JSX片段, 最终执行结果会确定渲染哪一个片段,只有执行结果的片段的uuid会被写入uiDes。这和编译时方案的静态识别有着本质的区别。
结语"Talk is cheap. Show me your code!" 这仅仅是一个思路?还是已经有落地完整的实现呢?
是有完整的实现的,alita项目在处理JSX语法的时候,采用的就是这个思路,这也是alita基本不限制写法却可以转化整个React Native项目的原因,另外alita在这个思路上做了很多优化。如果对这个思路的具体实现有兴趣,可以去研读一下alita源码,它完全是开源的https://github.com/areslabs/alita。
当然,你也可以基于这个思路,构造出自己的React小程序开发方案。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/104739.html
摘要:是的语法,截止我写这篇文章为止,小程序还是不支持语法的,所以需要使用这个库下载,并把文件夹放到目录下在引入封装,让微信的支持语法所有的请求,默认携带可以控制是否显示加载状态加载中封装好后就可以在文件中使用了,使用方法如下请 async-await是ES7的语法,截止我写这篇文章为止,小程序还是不支持async-await语法的,所以需要使用regenerator这个库 下载regene...
摘要:官方规范估计很难出现现代框架的设计了,因为官方设计中前端三剑客是相互分离的方案,为了解决现阶段前端框架的问题,必须由完全接管,这几乎就是,或者支持语法的,可这与最初网页设计思路是违背的。现代前端框架正在告诉我们新的三剑客虚拟虚拟。 1 引言 深入思考为何前端需要框架,以及 web components 是否可以代替前端框架? 原文地址,建议先阅读原文,或者阅读概述。 2 概述 现在前端...
摘要:也可以安装浏览器插件或,在某个文件的网页上就能看到一个的按钮,点击即可。 【AIS-TXD前端技术月刊】- 本月热门前端技术快报,汇聚 Github Trending 流行 Repo 和热门文章,文末有福利 showImg(https://segmentfault.com/img/remote/1460000018406928?w=1352&h=808); 欢迎 订阅 & 投稿本期小编...
阅读 2440·2021-11-17 09:33
阅读 708·2021-11-04 16:13
阅读 1308·2021-10-14 09:50
阅读 665·2019-08-30 15:53
阅读 3638·2019-08-30 14:18
阅读 3251·2019-08-30 14:14
阅读 2071·2019-08-30 12:46
阅读 3165·2019-08-26 14:05