メインコンテンツへ移動

Taro

無料2018-12-08#Solution#Taro教程#Taro入门#Taro原理#Taro源码分析#Taro与mpvue

一種のマルチエンドコード変換方案。微信ミニプログラム、Web、ReactNative、百度ミニプログラム、支付宝ミニプログラム、头条ミニプログラム、クイックアプリなどをサポート

一.目標定位

React 構文規範に従うマルチエンド統一開発フレームワーク

一種のマルチエンドコード変換方案。ここでの「エンド」とは微信ミニプログラム、Web、ReactNative、百度ミニプログラム、支付宝ミニプログラム、头条ミニプログラム、クイックアプリなどを指します

具体的には、1 部の React 風ソースコードを、「コンパイル」を通じてターゲットエンドと互換性のある形式に変換します。つまり:

             変換
nerv 業務コード ------> xx ミニプログラム業務コード +
                    Web 業務コード +
                    ReactNative 業務コード

目的は開発コストを低減し、効率を向上させることです:

元々 1 エンドでのみ実行できたプロジェクトにマルチエンド実行能力を持たせ、開発者のリファクタリングコストを低減する。

二.思路探索

初衷

React で微信ミニプログラムを書く。

微信ミニプログラムのネイティブ方式での開発は 非常に大変 だったため、React で微信ミニプログラムを開発したいと考えました

延伸

React 業務コードを微信ミニプログラムコードに変換するという最初のニーズを実現した後、同様の変換思路でマルチエンドに適応できることを発見しました。つまり1 対 1 から 1 対 n に延伸しました:

P.S. その中で Nerv は一種の React 風フレームワークで、API は React と類似しています

P.S. Taro コンポーネントライブラリが微信ミニプログラムを基準としたのも初衷によるものです(すでに完成したものを無駄にはできません)

思路

1 部のコードで n エンドを制覇するには、2 種類の思路しかありません:

  • 直接 1 エンドから n - 1 エンドに変換

  • 1 層の抽象を追加し、この抽象層から n エンドに変換

Bash と Batch(Windows バッチ処理スクリプト)を例に、1 部のスクリプトだけで*nix でも Windows でも実行したい場合、第 1 の思路では 1 つのものだけを実装する必要があります(bash から n - 1 エンドに変換):

function bash2batch(bash) {
  // ...
  return equivalentBatch;
}

または(batch から n - 1 エンドに変換):

function batch2bash(batch) {
  // ...
  return equivalentBash;
}

AtoB を実現できれば、1 部の AAB に適応できますが、「硬」転換は通常比較的困難 です。そのため Bash と Batch のシーンでは、第 2 の思路の実装が誕生しました:

Batsh: A language that compiles to Bash and Windows Batch.

つまり 1 層の抽象 C を追加し、それぞれ CtoACtoB を実装し、Batsh という抽象層から n エンドに変換します:

// 1.抽象層 Batsh を定義
const batsh = 'Neither bash nor batch';
// 2.抽象層から 2 エンドへの変換を実装
function batsh2batch(batsh) {
  // ...
  return equivalentBatch;
}
function batsh2bash(batsh) {
  // ...
  return equivalentBash;
}

同様に、Taro も第 2 の思路を採用しました。この抽象層が Taro 業務コードです:

P.S. Taro 業務コードは図中の Nerv コードですが、Taro コードと呼ぶ方がより正確です。Taro 固有の API サポート(Taro.getEnv() など)が追加されており、Nerv のスーパーセットだからです

三.核心実装

微信ミニプログラムを例に、それは 4 部分で構成されます:

  • 設定(JSON)

  • テンプレート(WXML)

  • スタイル(WXSS)

  • ロジック(JS)

設定とスタイルについては言うべきことはあまりありません。難点はテンプレートの変換とロジックの変換にあります

P.S. ReactNative スタイル変換は別問題で、これも難題です。RN はセレクタ、属性名/値およびデフォルト値、さらには CSS 特性サポート程度において大きな差異があるためです

コンパイル変換

1 部のコード A を別のコード B に変換するには、3 つのことを行う必要があります:

  1. コード A を解析して抽象記述(AST)を生成

  2. いくつかのマッピングルールに従って AST を操作し、新しい AST を生成

  3. 新しい AST に基づいてコード B を生成

taro-compile

P.S. コンパイル変換に関する詳細情報は、再看编译原理 と [Babel 快速指南](/articles/babel 快速指南/) を参照

テンプレートの変換

JSX 構文をミニプログラムで実行可能な文字列テンプレートに変換する。

JSX を入力:

render() {
  const { percent } = this.state;

  return (
    <View className='index'>
      <Button className='add_btn' onClick={this.props.add}>+</Button>
      { percent && <MyProgress percent={percent} strokeWidth={6} color='#FF4949' /> }
    </View>
  );
}

@tarojs/transformer-wx で変換後、微信ミニプログラムテンプレートを出力:

<block>
  <view class="index">
    <button class="add_btn" bindtap="funPrivatesBrJC">+</button>
    <block wx:if="{{percent}}">
      <my-progress percent="{{percent}}" strokeWidth="{{6}}" color="#FF4949"></my-progress>
    </block>
  </view>
</block>

ViewButton などはTaro 内蔵コンポーネントです:

Taro は微信ミニプログラムコンポーネントライブラリを基準に、jsx 構文規範と結合して、独自のコンポーネントライブラリ規範をカスタマイズした

関連 package は以下の通り:

  • @tarojs/components:Web 環境 Nerv コンポーネントライブラリをサポート。コンパイルを通じてターゲットプラットフォームのネイティブタグ/コンポーネントに置換

  • @tarojs/taro-components-rn:ReactNative 環境をサポートする React コンポーネントライブラリ(ReactNative コンポーネントライブラリが独立しているのは、差異が大きく、コンパイル手段で変換を実現するのが難しいためかもしれません)

すべてターゲットエンドのネイティブコンポーネントに変換されます:

ミニプログラムエンドでは、すべてのミニプログラムネイティブコンポーネントを使用でき、他のエンドでは、対応するコンポーネントライブラリ実装を提供する

しかしカスタムコンポーネント my-progress は微信ミニプログラムには存在しないため、予想通りに実行できません

必ずクロスエンドコンポーネント定義が必要になります。为此 Taro は 2 つのものを提供しました:

  • クロスエンドコンポーネントライブラリ Taro UI

  • カスタムコンポーネントを各ターゲットエンドがサポートする形式にパックすることをサポート(詳細は 基于 Taro 开发第三方多端 UI 库 を参照)

前者は有無の問題を解決し、一般应用场景に対応します。後者はカスタマイズが必要なシーンに対応するためにカスタマイズ能力を開放します

ロジックの変換

コンポーネントライブラリが多端適応を必要とするのと同様に、各エンドの能力差異も同様に適応が必要です:

コンポーネントライブラリおよびエンド能力は、異なるエンドで異なる実装を行うことで差異を平準化する

ランタイムフレームワークが各エンド能力を適応し、その上で実行される Taro 業務コードをサポートします。主に 3 つの作用があります:

  • コンポーネント化方案、設定オプションなどの基礎 API を適応

  • プラットフォーム能力関連の API を適応(ネットワークリクエスト、支払い、撮影など)

  • アプリケーション級特性を提供。イベントバス(Taro.EventsTaro.eventCenter)、実行環境関連 API(Taro.getEnv()Taro.ENV_TYPE)、UI 適応方案(Taro.initPxTransform())など

実装上、@tarojs/taro は API 適応の統一入口で、コンパイル時にプラットフォーム別に置換します:

  • @tarojs/taro単なる空殻で、API シグネチャを提供するのみ

プラットフォーム適応関連の package は 6 つあります:

P.S. コンポーネントライブラリ適応方案と異なり、API はコンパイル変換という道を諦め、直接全体を置き換えます

実際、1 部の業務コードだけを維持したい場合、Taro が提供する API は必ずn エンド API の和集合になります。例えば:

// 各ミニプログラムがサポートする API
Taro.setStorage()
// 百度ミニプログラム専用 API
Taro.textToAudio()
// 支付宝ミニプログラムと微信ミニプログラムでパラメータ処理に差異がある API
Taro.getStorageSync()
// ...

これらの API は直接使用でき、現在のプラットフォームがサポートしているか気にする必要はありません。ランタイムフレームワークの適応作業の一部がプラットフォーム能力 API 差異を平準化するためです。例えば:

H5 エンドではスキャンコード、Bluetooth などのエンド能力を呼び出せない

微信ミニプログラム標準を採用しているため、これらの API は H5 エンドで実行するときに何もしない。

同時に業務層でターゲット環境を区別し、これらのプラットフォーム関連コードが予想されるターゲット環境でのみ実行されることを保証します:

  • コンパイル時:process.env.TARO_ENV

  • ランタイム時:Taro.getEnv()

例えば:

// プラットフォーム別に API を呼び出し
if (process.env.TARO_ENV === 'swan') {
  Taro.textToAudio()
}
// プラットフォーム別に異なるコンポーネントを使用
<View>
  {process.env.TARO_ENV === 'weapp' && <ScrollViewWeapp />}
  {process.env.TARO_ENV === 'h5' && <ScrollViewH5 />}
</View>

P.S. コンパイル時の静的環境区別で大多数のシーンに対応できます。ランタイム時の環境区別は 不時の需要 のために用意されています

四.構造

設計上、Taro 方案は 3 層に分かれます:

業務層(React 風コード)
---------------------
変換層(JSX を微信ミニプログラムに変換)
---------------------
適応層 コンポーネントライブラリ(n エンドネイティブコンポーネントを適応)
      ランタイムフレームワーク(n エンド API 能力を適応)
---------------------

さらに、

  • エコシステム:UI ライブラリ、ルーティング、データフロー管理、CSS 前処理など

  • ビルド:Web は Webpack、ReactNative は Expo の xdl、残りは各自の IDE を使用

  • Lint:変換層がサポートしない書き方について、静的検査を通じて一部の警告を提供

五.ソースコード簡析

具体的実装に対応し、各部分に対応する package は以下の通り(taro/packages/):

// 変換
babel-plugin-transform-jsx-to-stylesheet
taro-plugin-babel
taro-plugin-csso
taro-plugin-uglifyjs
taro-transformer-wx

// 適応 - コンポ��ネントライブラリ
taro-components-rn
taro-components

// 適応 - ランタイムフレームワーク
taro-alipay
taro-h5
taro-qapp
taro-rn
taro-swan
taro-tt
taro-weapp
taro

// エコシステム
postcss-plugin-constparse
postcss-pxtransform
postcss-unit-transform
taro-async-await
taro-mobx-common
taro-mobx-h5
taro-mobx-prop-types
taro-mobx-rn
taro-mobx
taro-plugin-less
taro-plugin-sass
taro-plugin-stylus
taro-plugin-typescript
taro-redux-h5
taro-redux-rn
taro-redux
taro-router-rn
taro-router

// ビルド
taro-cli
taro-rn-runner
taro-webpack-runner

// Lint
eslint-config-taro
eslint-plugin-taro

// その他(公共方法)
taro-utils

さらに、面白いものがあります:

// 微信ミニプログラムから Taro に変換
taroize
// taroize 後のランタイム
taro-with-weapp

逆変換は別の扉です。変換而言、1 対 1 から 1 対 n に延伸した後、次の段階は n から 1 です。つまり:

// ターゲットエンド
A = weapp
B = ReactNative
C = ReactNative
// 抽象層
T = Taro

// 第 1 段階:1 対 1
T2A()
// 第 2 段階:1 対 n
T2A(), T2B(), T2C()...
// 第 3 段階:n から 1
A2T(), B2T(), C2T()...

第 3 段階が完成すれば、天下大同になります(何でも n エンドに変換できます)

P.S. 現在(2018/12/9)、A2T()(ミニプログラムコードから Taro に変換)はリリース待ちです。詳細は バージョン計画 を参照

六.制限

制限方面で最も深く感じるのは JSX です。JSX の柔軟性は驚異的で(動的コンポーネント、高階コンポーネント)、同時に微信ミニプログラムのテンプレート構文は制限が非常に多い(WXS というパッチで一部の能力を強化しましたが)ため、調和できない矛盾が発生します。そのため:

JSX の書き方は極めて柔軟で変化に富んでいる。我々は列挙方式で、常用的な、React 公式が推奨する書き方を変換ルールとしてサポートし、一些比較的珍しい、またはそれほど推奨されない書き方はサポートせず、代わりに eslint プラグイン方式で、ユーザーに変更を提示する

具体的には、JSX 制限は以下の通り:

  • 動的コンポーネント をサポートしない
  • JSX 要素を含む map ループ内で if 式を使用できない
  • Array#map 以外の方法で JSX 配列を操作できない
  • JSX パラメータ内で無名関数を使用できない
  • JSX パラメータ (props) に JSX 要素を渡すことを許可しない
  • class コンポーネントのみをサポート
  • 暫く render() 以外の方法で JSX を定義することをサポートしない
  • JSX パラメータ内でオブジェクト展開演算子を使用できない
  • 状態なしコンポーネント(関数型コンポーネント)をサポートしない
  • props.children は渡すことのみでき操作できない
  • ...

これらの変換制限について、補完性方案は Lint 検査エラーで、代替方案を提供します

JSX 以外に、2 つの大きな制限があります:

  • CSS:ReactNative の CSS サポート程度に制限される(flex レイアウトのみ使用可能)

  • タグ:HTML タグを使用しないことを約定(すべてマルチエンド適応済みの内蔵コンポーネントを使用。ViewButton など)

P.S. 静的変換自身の制限により、多くの変換は実現できません

七.应用场景

業務が同時に異なるエンドで表現を要求される場合、異なるエンドに向けて複数セットのコードを書くコストは明らかに非常に高い

つまり、同一業務がマルチエンドで重なり合うニーズがある場合、Taro などのマルチエンドコード変換方案に意味があります

もう 1 つのシーン類は Taro が最初に解決したい微信ミニプログラム開発体験問題です。Taro で微信ミニプログラムを開発すると、うっかりマルチエンドにも適応できるため、これも不错的选择です

参考資料

コメント

コメントはまだありません

コメントを書く