taro-convert 转换流程

taro-convert 是一个命令行工具,其作用是将原生微信小程序转换成 taro 标准代码。其主要是通过src/convertor/index.ts 中的 Convertor类来实现。

初始化

在生成Convertor对象时,除了一些必要的初始化工作外,比如初始化项目根路径,生成输出文件夹等等。其还会去解析微信小程序工程。主要包括如下几方面:

  1. 读取入口文件 app.js 路径。

  2. 读取配置文件 app.json,获取如下信息:

    • pages,获取路由信息,存放到页面路由集合中。
    • sitemapLocaiton,若存在,则将 sitemap.json 拷贝到转换输出目录。
    • subpackages/subPackages,获取 subpackages 信息,添加到路由页面集合中。
  3. 读取样式文件 app.wxss

转换过程

转换流程分为以下三个步骤:

  • 入口文件的转换
  • 页面文件的转换,逐个遍历页面文件,进行转换。即在 app.json 中定义的 pages
  • 根据模板生成工程配置文件

js 代码转换

主要分为入口文件App.js和导入文件的转换。

入口文件转换

主要是在 generateEntry 方法中,其内部调用了generateScriptFiles 处理导入的文件。

主要步骤如下:

  1. 调用 taroize 生成 astwxml 中导入的模板信息。taroize 的内部处理下面会有说明。
  2. 调用 parseAst 遍历并修改 ast,并且统计 importjs 文件,作为结果返回。
  3. 调用 generateMinimalEscapeCode,根据 ast 生成代码,并写入文件到输出目录。
  4. 若入口有样式文件,则调用 traverseStyle 进行处理。
  5. 若入口文件有 import 其他 js 文件,则继续调用 generateScriptFiles 进行处理。内部逻辑在下面会讲到。
  6. 若入口配置文件中有定义 tabBar,则调用 generateTabBarIcon 进行处理。将 Icon/selectedIcon 复制到输出目录。

导入文件转换

导入文件指的是通过 require/import 引入的文件。其转换逻辑主要在 generateScriptFiles 方法,入参为 js 文件路径集合。

主要步骤如下:

  1. 遍历每个 js 文件。
  2. 调用 wxTransformer 生成 ast。其内部逻辑很复杂,但是对于微信小程序来说,只是简单返回其 ast,无其他处理。
  3. 调用 parseAst 遍历并修改 ast,并返回 importjs 文件。其逻辑下面会有说明。
  4. 调用 generateMinimalEscapeCode 生成代码,写入文件。
  5. 因为当前处理的 js 文件可能也 import 了其他 js 文件,所以递归调用 generateScriptFiles 进行处理。

taroize 方法

taroize 是比较重要的一个方法,会进行一系列的预处理。其参数中包含了配置信息 xx.jsonjs 文件内容,wxml 模板信息。具体处理如下:

  1. parseXML

    如果传入了 xml,则进行解析,包括标签的解析。

  2. parseJSON

    json 加上括号包装成表达式 (json),调用 babeltemplate 方法返回对象表达式的节点信息 ObjectExpression。会用在后续的 parsePage 中(下面会提到),主要用于填充新生成类中的 config 的值。

    比如 json{'title': 'hello'},则会返回下图中红框部分。

image.png
  1. parseScript

    设置一系列babel插件参数,将代码转换为 ast。然后做如下处理:

    1. 自定义 vistior 进行访问。

      • 遇到 BlockStatement,即方法具体实现 {}, 将 作用域scope 的值从 wx 修改为 Taro

        也就是说如果在 xx.js 最外层定义了 function,会走到这里。如下:

        //获取应用实例
        const app = Taro.getApp()
        
        function test() {
          console.log('test')
        }
        
      • 遇到 Identifier 标识符,且节点名为 wx,则替换为 Taro

      • 遇到 CallExpression 函数调用,进行标识符、属性调用的替换以及类的生成。

        1. 标识符替换

          如果IdentifiergetApp/getCurrentPages,替换成 Taro.getApp/Taro.getCurrentPages 调用。

        2. 属性调用替换

          如果为 wx.xxx 的调用,则替换为 Taro.xxx

        3. Page/Component/App 方法调用,类的生成

          如果是 Page/Component/App 方法,则说明其为微信小程序·。调用 parsePage 返回一个 ClassDecleration 结构,插入到 body 中。这样生成的文件中就会包含如下信息:

          @withWeapp({ xx })
          class _C extends Taro.Component {
               config = {
                 navigationBarTitleText: '查看启动日志'
               }
               
               render () {
               }
          }
          

          其中 parsePage中包括 configrender 方法,装饰器 withWeapp 的生成。config 为配置文件 json 中的信息。

          {
              "navigationBarTitleText": "查看启动日志",
              "usingComponents": {}
          }
          
    2. 如果不为微信小程序,则在 js 文件末尾默认加上 ;Component({}),再进行以上处理。

    3. 导入使用的组件和依赖文件,结果如下所示:

       ```
       import { Block, View, Button, Image, Text } from '@tarojs/components'
       import Taro from '@tarojs/taro'
       import withWeapp from '@tarojs/with-weapp'
       ```
      

      其中使用的组件是通过解析 wxml中的标签来计算,比如以下 xml 代码:

       ```
       <view class="container log-list">
           <block wx:for="{{logs}}" wx:for-item="log">
               <text class="log-item">{{index + 1}}. {{log}}</text>
           </block>
       </view>
       ```
      

      则会自动引入 BlockViewText 组件,其中 Block是默认就会引入的,无论在 xml 中是否使用。

parseAst 方法

可使用 https://astexplorer.net/ 查看语法树。

主要用来遍历 ast,对其进行修改,并统计 importjs 文件信息。其中语法树的修改包括头部导入 Taro 框架,调用对象的替换为Taro 等等。

首先使用 traverse 进行 ast 遍历,分为 enterexit两个过程。

enter 处理

enter 时会处理如下几种类型:

  1. ClassDeclaration,类的声明。

    内部会继续遍历 ClassMethod,类中定义的方法。如果遇到 render,继续遍历,如果存在 JSXElment,标记为 taro组件,并记录类名。

    一个 JSXElement 如下,相当于一个组件的声明。

    <Image src={require('../kkk')}></Image>
    
  2. ClassExpression,这里主要用于类被赋值给一个变量的情况,= 右边为 ClassExpression。内部处理方式同步骤1

        f = class NamedFoo {
          constructor() {}
          whoIsThere() {
            return NamedFoo.name;
          }
        }
    
  3. ExportDefaultDeclaration,处理 export default 的情况。同样包括两种情况。内部处理方式同步骤 1

    • export default class Component {}
    • export default a = class Component {}
  4. ImportDeclaration,处理 importjs/ts 文件。

    通过调用 analyzeImportUrl 分析引入的 js/ts 文件。

  5. CallExpression,处理函数调用。

    • 若调用了 require,则调用 analyzeImportUrl 分析出 importjs/ts 文件。
    • 若调用了微信小程序的 ['getApp', 'getCurrentPages', 'requirePlugin'] 这几个方法,则会替换成 Taro 调用,并标记需要引入 Taro 框架。
  6. MemberExpression,处理对象属性调用。
    如果调用对象为 wx,则替换成 Taro。同时标记需要导入 Taro 框架。

    wx.setStorageSync('logs', logs)  => Taro.setStorageSync('logs', logs)
    
exit 处理

exit 中做如下处理。

  1. 处理body节点,如果需要导入 @tarojs/taro,则加入importDeclaration节点。

    import Taro from '@tarojs/taro'
    
  2. 遍历 StringLiteral,主要处理图片路径引用。

    • 计算其绝对路径, 并将图片复制到输出目录。重新计算源文件与图片的相对路径 relativePath

    • 如果其父节点是变量声明(值为图片路径),则使用 require(relativePath)进行替换。

    • 如果其父节点是 JSX 属性,则使用 jSXExpressionContainer 进行替换,即{require(relativePath)}表达式的方式进行替换。

      在下例中, src 就是 JSX 属性。

      <Image src={require('../kkk')}></Image>
      
  3. 导入依赖文件,包括引入样式和其他依赖

    导入样式:

    import './app.scss'
    

    导入依赖组件和文件:

    import xx from 'xx'
    

    其中依赖的组件是从页面配置文件的 usingComponent 中解析出来的。

  4. 如果判断 isApptrue,则会在最后插入一段代码。

    Taro.render(<App />, document.getElementById('app'))
    

    但只有在解析入口文件时, isApp 才为 true

样式转换

通过 postcss,传入自己写的postcss-taro-unit-transform插件来进行转换的。其内部实现很简单,主要做了两件事情:

  • 10px 转换为 20px,即将数值 * 2
  • rpx 替换为 px

页面转换

包括js 文件转换 + 样式转换 + 依赖组件递归转换。与入口文件的转换类似,多了页面模板 wxml + 依赖组件的转换

其会从页面配置信息中拿到出依赖的组件信息。

组件转换

同页面转换。

根据模板生成配置文件

根据对应文件的模板生成相应的内容。

配置文件

  • index.js:通用配置
  • dev.js:开发配置,会合并 index.js
  • pro.js:发布配置,会合并 index.js
  • .gitignore
  • .editorconfig:编辑器相关配置
  • .eslintrceslint 相关配置
  • project.config.json:工程配置
  • package.json
  • src/index.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342