• 候选企业:中国东方航空集团公司 2019-10-20
  • 水乡“大花园”迎来投资热 2019-10-16
  • 非“急刹车、一刀切” 权威部门力促光伏行业健康发展 2019-10-16
  • 重庆市武隆区:建设“幸福候鸟”工程 2019-10-06
  • 言而无信似乎成了特朗普政府的特质与标致 2019-10-04
  • 深圳多所学校现“水泥跑道” 家长担心孩子摔伤 2019-10-04
  • 新余市市场和质量监管局为重病村级食品安全信息员捐款(图) 2019-10-02
  • 浙江现奇葩“失恋展” 2019-09-29
  • 我发现从五+年代农业用化肥农药,在六+年代几百年长的柿树几乎死光。没人研究! 2019-09-29
  • 警惕套路贷 放贷是幌子 建议增设非法放贷罪 2019-09-28
  • 哭泣的是鸿茅药酒,受伤的是中华医药!(原创首发) 2019-09-28
  • 天津对口援建西藏昌都 一批项目签约落地 2019-09-25
  • 为焦虑买单,真的能获得平静吗? 2019-09-25
  • 热刺中场埃里克森领衔 北欧童话能否续写逆袭新篇 2019-09-23
  • 葡萄牙首都上演城市节狂欢 2019-09-20
  • 大乐透走势基本走势图:浅谈一种让小程序支持JSX语法的新思路

    文章TAG:小程序 JSX语法
    时间:2019-06-16来源/作者:ykforerlang 编辑:源码库 文章热度:

    山西十一选五遗漏10 www.fqjb.net React社区一直在探寻使用React语法开发小程序的方式,其中比较著名的项目有Taro,nanachi。而使用React语法开发小程序的难点主要就是在JSX语法上,JSX本质上是JS,相比于小程序静态模版来说太灵活。本文所说的新思路就是在处理JSX语法上的新思路,这是一种更加动态的处理思路,相比于现有方案,基本上不会限制任何JSX的写法,让你以真正的React方式处理小程序,希望这个新思路可以给任何有志于用React开发小程序的人带来启发。

    现有思路的局限

    在介绍新的思路之前,我们先来看下Taro(最新版1.3),nanachi是怎么在小程序端处理JSX语法的。简单来说,主要是通过在编译阶段把JSX转化为等效的小程序wxml来把React代码运行在小程序端的。

    举个例子,比如React逻辑表达式:

    xx && <Text>Hello</Text>

    将会被转化为等效的小程序wx:if指令:

    <Text wx:if="{{xx}}">Hello</Text>

    这种方式把对JSX的处理,主要放在了编译阶段,他依赖于编译阶段的信息收集,以上面为例,它必须识别出逻辑表达式,然后做对应的wx:if转换处理。

    那编译阶段有什么问题和局限呢?我们以下面的例子说明:

    class App extends React.Component {
      render () {
        const a = <Text>Hello</Text>
        const b = a
    
        return (
          <View>
            
          </View>
        )
      }
    }

    首先我们声明 const a = <Text>Hello</Text>,然后把a赋值给了b,我们看下最新版本Taro 1.3的转换,如下图:

    小程序,JSX语法

    这个例子不是特别复杂,却报错了。

    要想理解上面的代码为什么报错,我们首先要理解编译阶段。本质上来说在编译阶段,代码其实就是‘字符串',而编译阶段处理方案,就需要从这个‘字符串'中分析出必要的信息(通过AST,正则等方式)然后做对应的等效转换处理。

    而对于上面的例子,需要做什么等效处理呢?需要我们在编译阶段分析出bJSX片段:b = a = <Text>Hello</Text>,然后把<View></View>中的等效替换为<Text>Hello</Text>。然而在编译阶段要想确定b的值是很困难的,有人说可以往前追溯来确定b的值,也不是不可以,但是考虑一下 由于b = a,那么就先要确定a的值,这个a的值怎么确定呢?需要在b可以访问到的作用域链中确定a,然而a可能又是由其他变量赋值而来,循环往复,期间一旦出现不是简单赋值的情况,比如函数调用,三元判断等运行时信息,追溯就宣告失败,要是a本身就是挂在全局对象上的变量,追溯就更加无从谈起。

    所以在编译阶段 是无法简单确定b的值的。

    我们再仔细看下上图的报错信息:a is not defined。

    小程序,JSX语法

    为什么说a未定义呢?这是涉及到另外一个问题,我们知道<Text>Hello</Text>,其实等效于React.createElement(Text, null, 'Hello'),而React.createElement方法的返回值就是一个普通JS对象,形如

    // 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,所以<Text>Hello</Text>这个JSX片段被特殊处理了,a不再是一个普通js对象,这里我们看到a变量甚至丢失了,这里暴露了一个很严重的问题:代码语义被破坏了,也就是说由于编译时方案对JSX的特殊处理,真正运行在小程序上的代码语义并不是你的预期。这个是比较头疼。

    新的思路

    正因为编译时方案,有如上的限制,在使用的时候常常让你有“我还是在写React吗?”这种感觉。

    下面我们介绍一种全新的处理思路,这种思路在小程序运行期间和真正的React几无区别,不会改变任何代码语义,JSX表达式只会被处理为React.createElement方法调用,实际运行的时候就是普通js对象,最终通过其他方式渲染出小程序视图。下面我们仔细说明一下这个思路的具体内容。

    第一步:给每个独立的JSX片段打上唯一标识uuid,假定我们有如下代码:

    const a = <Text uuid="000001">Hello</Text>
    
    const y = <View uuid="000002">
      <Image/>
      <Text/>
    </View>

    我们给a片段,y片段 添加了uuid属性

    第二步:把React代码通过babel转义为小程序可以识别的代码,例如JSX片段用等效的React.createElement替换等

    const a = React.createElement(Text, {
     uuid: "000001"
    }, "Hello");

    第三步:提取每个独立的JSX片段,用小程序template包裹,生成wxml文件

    <template name="000001">
      <Text>Hello</Text>
    </template>
    
    <template name="000002">
      <View uuid="000002">
        <Image/>
        <Text/>
      </View>
    </template>
    
    
    <!--占位template-->
    <template is="{{uiDes.name}}" data="{{...uiDes}}"/>

    注意这里每一个template 的name标识和 JSX片段的唯一标识uuid是一样的。最后,需要在结尾生成一个占位模版:<template is="{{uiDes.name}}" data="{{...uiDes}}"/>。

    第四步:修改ReactDOM.render的递归(React 16.x之后,不在是递归的方式)过程,递归执行阶段,聚合JSX片段的uuid属性,生成并返回uiDes数据结构。

    第五步:把第四步生成的uiDes,传递给小程序环境,小程序把uiDes 设置给占位模版<template is="{{uiDes.name}}" data="{{...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文件:

    <template name="000001">
      <Text>Hello</Text>
    </template>
    
    <template name="000002">
      <View>
        <template is="{{child0001.name}}" data="{{...child0001}}"/>
      </View>
    </template>
    
    <!--占位template-->
    <template is="{{uiDes.name}}" data="{{...uiDes}}"/>

    使用我们定制之后render执行ReactDOM.render(<App/>, parent)。在render的递归过程中,除了会执行常规的创建组件实例,执行生命周期之外,还会额外的收集执行过程中组件的uuid标识,最终生成 uiDes 对象

    const uiDes = {
      name: "000002",
      
      child0001: {
          name: 000001,
          ...
      }
      
      ...
    }

    小程序获取到这个uiDes,设置给占位模版<template is="{{uiDes.name}}" data="{{...uiDes}}"/>。 最终渲染出小程序视图。

    小程序,JSX语法

    在这整个过程中,你的所有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小程序开发方案。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持ASPKU源码库。


    注:相关教程知识阅读请移步到微信小程序开发频道。
    相关微信小程序开发
    热门标签

    微信小程序开发Rss订阅微信小程序开发搜索

  • 候选企业:中国东方航空集团公司 2019-10-20
  • 水乡“大花园”迎来投资热 2019-10-16
  • 非“急刹车、一刀切” 权威部门力促光伏行业健康发展 2019-10-16
  • 重庆市武隆区:建设“幸福候鸟”工程 2019-10-06
  • 言而无信似乎成了特朗普政府的特质与标致 2019-10-04
  • 深圳多所学校现“水泥跑道” 家长担心孩子摔伤 2019-10-04
  • 新余市市场和质量监管局为重病村级食品安全信息员捐款(图) 2019-10-02
  • 浙江现奇葩“失恋展” 2019-09-29
  • 我发现从五+年代农业用化肥农药,在六+年代几百年长的柿树几乎死光。没人研究! 2019-09-29
  • 警惕套路贷 放贷是幌子 建议增设非法放贷罪 2019-09-28
  • 哭泣的是鸿茅药酒,受伤的是中华医药!(原创首发) 2019-09-28
  • 天津对口援建西藏昌都 一批项目签约落地 2019-09-25
  • 为焦虑买单,真的能获得平静吗? 2019-09-25
  • 热刺中场埃里克森领衔 北欧童话能否续写逆袭新篇 2019-09-23
  • 葡萄牙首都上演城市节狂欢 2019-09-20
  • 今日福彩3d字谜总汇 江西11选五5开爱彩乐奖结果 福彩快3下载 时时彩走势图近100期 5分赛车计划网址 王者荣耀 2304期七星彩规律版 江苏福彩快3走势图下载 15选5最新杀号360 体彩大乐透带线走势图下载 重庆幸运农场运营时间 赌场玩牛牛的技巧 任选9分析 狂飙摩托电影在线 利来资源在线