導入
今日の成長するミニプログラム市場では、ミニプログラムの数が急速に増加しています。これは、ミニプログラムには軽量、利便性、優れたユーザーエクスペリエンスなどの多くの利点があり、この分野に参加する開発者や企業がますます増えているためです。ミニプログラムの人気に伴い、ユーザーの多様なニーズに応えるために、さまざまな業界が独自のミニプログラムを立ち上げています。
しかし、ミニ プログラム市場の多様性と急速な発展により、各ミニ プログラム クライアントの API の違いが非常に大きくなりました。さまざまなミニ プログラム プラットフォームは、独自の特別なニーズや機能の位置付けを満たすために API をカスタマイズおよび調整することがよくあります。これにより、さまざまなミニ プログラム クライアント間で API に違いが生じ、異なるプラットフォームの開発者は異なる API を開発して適応する必要があります。
開発者にとって、さまざまなプラットフォーム向けに一連のミニプログラム アプリケーションを再開発することは、終わりのない悪夢となるでしょう。開発者は、各クライアントの API の違いを熟知して習得し、多くの繰り返しコードを作成し、プラットフォーム固有のデバッグと適応作業を実行する必要があります。これにより、開発作業量と時間コストが増加するだけでなく、エラーや互換性の問題が発生しやすくなります。
この状況において、Taro の登場は開発者に解決策を提供します。開発フレームワークとコンポーネントの統一されたセットを提供することにより、開発者は複数の小さなプログラム プラットフォームで同時に実行できるコードのセットを作成できます。Taro のコンパイル ツールは、開発者のコードをさまざまなプラットフォームで必要なコードに変換できるため、クロスプラットフォームの開発と適応が可能になり、開発者の負担が軽減され、開発効率が向上します。
Taro は、React 構文仕様に準拠したマルチターミナル統合開発フレームワークです (追記: Vue 構文もサポートされています)。主にクロスプラットフォーム アプレット、H5、モバイル アプリケーションを構築するために使用されます。市場には他にも次のようなマルチターミナル フレームワークがありますが、これらに限定されません。
-
ユニアプリ:
-
uni-app は、DCloud によって開始された Vue.js に基づくクロスプラットフォーム開発フレームワークで、WeChat ミニプログラム、Alipay ミニプログラム、H5、App などの複数のプラットフォーム用のアプリケーションを構築するために使用できます。
-
リアクトネイティブ
-
React Native は、ネイティブ モバイル アプリケーションを構築するために Facebook によって開発されたフレームワークです。JavaScript と React 構文を使用するため、開発者は単一のコード セットを通じて iOS と Android の両方でアプリケーションを構築できます。
-
プロジェクトのアドレス: React Native Chinese Web サイト · React を使用してネイティブ アプリケーション用のフレームワークを作成する
-
フラッター
-
Flutter は、クロスプラットフォームのモバイル、Web、デスクトップ アプリケーションを構築するために Google によって開発された UI ツールキットです。Dart プログラミング言語を使用し、豊富な UI コンポーネントとレンダリング機能を提供します。
-
プロジェクトアドレス: Flutter: すべての画面に興奮をもたらす - Flutter 中国の開発者 Web サイト - Flutter
-
ウィークス
-
Weex は、Alibaba によって開発されたクロスプラットフォーム開発フレームワークで、モバイル アプリケーションの構築に Vue.js 構文を使用します。iOS、Android、Web での実行をサポートしています。
-
プロジェクトのアドレス: Weex インキュベーション ステータス - Apache Incubator
-
ネイティブスクリプト
-
NativeScript は、ネイティブ モバイル アプリを構築するために Progress によって開発されたオープン ソース フレームワークです。JavaScript または TypeScript を使用したコードの作成をサポートし、ネイティブ API にアクセスする機能を提供します。
-
プロジェクトのアドレス:はじめに | NativeScript
上記のうち、小規模なプログラムのシナリオをサポートしているのは uni-app だけであり、マルチターミナル フレームワークの半分を占めています。
タロウの主人公
要約すると、Taro の主な機能と利点は公式声明を参照しています。「Taro を使用すると、一連のコードを記述し、その後、Taro のコンパイル ツールを使用してソース コードを個別にコンパイルし、異なる端末 (WeChat アプレット) でコンパイルすることしかできません。 、H5、アプリ側など)。
プロジェクトアドレス: Taro | ミニプログラムのクロスプラットフォーム統合開発ソリューション
一度コンパイルすれば複数の端末で実行可能
ここで、「コンパイル時構成」メカニズムを説明する必要があります。公式の「ワンタイムコンパイル」は、dist パッケージがすべてのプラットフォームで実行できることを意味するものではありません。
代わりに、対応する手順を使用して、実行するプラットフォームに適したパッケージを作成します。
例えば:
微信小程序 编译命令 yarn build:weapp
百度小程序 编译命令 yarn build:swan
支付宝小程序 编译命令 yarn build:alipay
H5 编译命令 yarn build:h5
RN 编译命令 yarn build:rn --platform ios
……
したがって、本当に注意する必要があるのは、Taro がターゲット プラットフォームに対して何をしたかということです。以下では、WeChat アプレットを例として取り上げます。
Taro フレームワークには、@tarojs/plugin-platform-weapp WeChat ミニ プログラム プラットフォーム プラグインに、対応するコンパイラーと構築ツールが組み込まれています。ここでWeChatミニプログラムプラットフォームの設定項目を登録します。
//taro-weapp/src/index.ts
//在此注册微信小程序平台
ctx.registerPlatform({
name: 'weapp',
useConfigName: 'mini',
async fn ({ config }) {
const program = new Weapp(ctx, config, options || {})
await program.start()
}
})
UnRecursiveTemplate を継承する WeChat アプレットという名前のテンプレート クラスを事前定義します。その主な機能は、Taro フレームワークでテンプレート関連の操作を処理し、特定のニーズに応じてカスタマイズすることです。
//taro-weapp/src/template.ts
export class Template extends UnRecursiveTemplate {
...
//构建wxs模板
buildXsTemplate () {
return '<wxs module="xs" src="./utils.wxs" />'
}
//创建小程序组件
createMiniComponents (components): any {
const result = super.createMiniComponents(components)
// PageMeta & NavigationBar
this.transferComponents['page-meta'] = result['page-meta']
this.transferComponents['navigation-bar'] = result['navigation-bar']
delete result['page-meta']
delete result['navigation-bar']
return result
}
//替换属性名称
replacePropName (name: string, value: string, componentName: string, componentAlias) {
...
}
//构建wxs模板中与焦点相关的方法,根据插件选项判断是否启用键盘附件功能,并返回相应的字符串
buildXSTepFocus (nn: string) {
...
}
//修改模板结果的方法,根据节点名称和插件选项对模板进行修改。
modifyTemplateResult = (res: string, nodeName: string, _, children) => {
...
}
//构建页面模板的方法,根据基础路径和页面配置生成页面模板字符串。
buildPageTemplate = (baseTempPath: string, page) => {
...
}
}
Taro のコンパイル ツールは、選択されたプラットフォームに応じて、対応するプラットフォームに必要なコードを変換します。ctx.applyPlugins を使用して、対応するプラットフォームのプラグイン処理関数を呼び出します。ここで、プラットフォーム パラメーターは、対応するプラットフォームを指定します。
//taro-cli/src/build.ts
...
await ctx.applyPlugins(hooks.ON_BUILD_START)
await ctx.applyPlugins({
name: platform,
opts: {
config: {
...config,
isWatch,
mode: isProduction ? 'production' : 'development',
blended,
isBuildNativeComp,
newBlended,
async modifyWebpackChain (chain, webpack, data) {
await ctx.applyPlugins({
name: hooks.MODIFY_WEBPACK_CHAIN,
initialVal: chain,
opts: {
chain,
webpack,
data
}
})
},
...
さらに、コード変換プロセスには次の作業も含まれます。
-
構文変換: Taro は、React と同様の JSX 構文を使用した開発をサポートしており、JSX コードを、小規模プログラム用の WXML、React Native のコンポーネントなど、さまざまなプラットフォームでサポートされている構文に変換します。
-
スタイル変換: Taro は、CSS プリプロセッサを使用した Sass、Less などのスタイルの記述をサポートしています。コンパイル プロセス中に、Taro はこれらのスタイル ファイルを、ミニ プログラム用の WXSS、H5 用の CSS など、さまざまなプラットフォームでサポートされるスタイル シートに変換します。
コンパイル プロセス中に、Taro は以下も実行します。
-
静的リソースの処理: Taro は、プロジェクト内の画像、フォントなどの静的リソース ファイルを処理し、さまざまなプラットフォームに適した形式に変換し、圧縮して最適化します。
-
ファイル コピー: Taro は、プロジェクト設定ファイル、静的ページなど、コンパイルを必要としないいくつかのファイルを出力ディレクトリに直接コピーします。
-
ファイルのマージと分割: Taro は、コードの読み込みパフォーマンスを向上させるために、コード内の構成と参照関係に基づいて複数のファイルをマージまたは分割します。
-
コードの圧縮と難読化: Taro は、生成されたコードを圧縮して難読化して、ファイル サイズを削減し、実行効率を向上させることができます。
クロスプラットフォームへの適応と差異の処理
異なるプラットフォームの API には常にいくつかの違いがあります。Taro はどのようにして API の適応と差別化を達成するのでしょうか?
Taro は、アダプテーション レイヤーや条件付きコンパイルなどのメカニズムを通じて API の適応と差別化を実装します。
これは、開発者がコードで使用できる API インターフェイスの統一セットを提供し、Taro はコンパイル プロセス中にこれらの API を各プラットフォームに適した特定の実装に変換します。
例えばgetLocation
。
位置決め機能を使用したい場合は、プロジェクトで Taro が提供する API を使用するだけです getLocation
。
Taro.getLocation().then(res => {
console.log(res.latitude, res.longitude);
});
コンパイル プロセス中に、Taro は、ターゲット プラットフォームの違いに基づいて、このコードをさまざまなプラットフォームに適した特定の実装に変換します。
WeChat アプレットの場合、WeChat アプレットに変換されると wx.getLocation
、元のパラメータとコールバック関数が保持されます。
wx.getLocation().then(res => {
console.log(res.latitude, res.longitude);
});
Alipay アプレットの場合、Taro はそれを Alipay アプレットに変換し my.getLocation
、元のパラメータとコールバック関数も保持します。
my.getLocation().then(res => {
console.log(res.latitude, res.longitude);
});
このように、Taro はコンパイル プロセス中に、ターゲット プラットフォームの違いに応じて、統合 API を各プラットフォームがサポートする特定の API に変換します。このコードでは、 processApis 関数は API のコレクションをパラメーターとして受け取り、その中の各 API を処理します。
//shared/native-apis.ts
function processApis (taro, global, config: IProcessApisIOptions = {}) {
...
apis.forEach(key => {
if (_needPromiseApis.has(key)) {
const originKey = key
taro[originKey] = (options: Record<string, any> | string = {}, ...args) => {
let key = originKey
// 第一个参数 options 为字符串,单独处理
if (typeof options === 'string') {
...
}
// 改变 key 或 option 字段,如需要把支付宝标准的字段对齐微信标准的字段
if (config.transformMeta) {
...
}
...
// 为页面跳转相关的 api 设置一个随机数作为路由参数。为了给 runtime 区分页面。
setUniqueKeyToRoute(key, options)
// Promise 化:将原本的异步回调形式转换为返回Promise对象的形式,使api的调用更加方便且符合现代JavaScript的异步处理方式。
const p: any = new Promise((resolve, reject) => {
obj.success = res => {
config.modifyAsyncResult?.(key, res)
options.success?.(res)
if (key === 'connectSocket') {
resolve(
Promise.resolve().then(() => task ? Object.assign(task, res) : res)
)
} else {
resolve(res)
}
}
obj.fail = res => {
options.fail?.(res)
reject(res)
}
obj.complete = res => {
options.complete?.(res)
}
if (args.length) {
task = global[key](obj, ...args)
} else {
task = global[key](obj)
}
})
// 给 promise 对象挂载属性
if (['uploadFile', 'downloadFile'].includes(key)) {
...
}
return p
}
} else {
...
}
})
...
}
ps: Taro は統一された API インターフェイスのセットを提供しますが、プラットフォームによっては特定の機能をサポートしていない場合があります。プラットフォーム固有の違いを処理するには、条件付きコンパイルを使用してプラットフォーム固有の API を呼び出すことが必要になる場合があります。
クロスプラットフォームの UI コンポーネント ライブラリ
Taro を使用してマルチターミナル プロジェクトを作成する場合は、 Taro が提供する View
Taro コンポーネントを使用する必要があります。
これらの Taro コンポーネントは、異なるプラットフォーム上の対応するネイティブ コンポーネントまたは要素に変換されるためです。
たとえば、次のコードでは、Taro が提供する Image、View、および Text コンポーネントを使用してビューを作成します。
import Taro from '@tarojs/taro';
import { View, Text, Image } from '@tarojs/components';
function MyComponent() {
return (
<View>
<Text>Hello</Text>
<Image src="path/to/image.png" />
</View>
);
}
コンパイルと生成のプロセス中に、Taro はターゲット プラットフォームの違いに基づいて、コンポーネントを各プラットフォームに適した特定のコンポーネントに変換します。たとえば、View
コンポーネントは WeChat アプレットのコンポーネントに変換されます view
。H5 の場合、View
コンポーネントは要素に変換されます <div>
。
WeChat ミニ プログラムでは:
<view>
<text>Hello</text>
<image src="path/to/image.png"></image>
</view>
H5 では:
<div>
<span>Hello</span>
<img src="path/to/image.png" />
</div>
このようにして、同じコードを使用してビューを作成できます。これは、公式が 1 セットのコードのみを作成することを意味します。
Taro は、抽象化レイヤー、プラットフォーム適応、クロスプラットフォーム コンパイル、その他のプロセスを通じて、実際にマルチターミナル コンポーネント ライブラリの実装への道を切り開きました。Taro-UI のような独自のマルチターミナル コンポーネント ライブラリを適応させたい場合。Taro が提供する基本コンポーネントを使用するだけで、複雑なコンポーネントを構築できます。
逆変換
以前に WeChat ミニ プログラムを作成したことがあり、今度は上司がそれを Alipay などのミニ プログラムにパラレル ポートしてほしいと言ってきたとします。コードをリファクタリングする時間がない場合は、逆変換によって危機を回避できる可能性があります。逆変換は、名前が示すように、小さなプログラムを Taro プロジェクトに変換することです。
関連するコードは @tarojs/cli-convertor パッケージにあり、コア ロジックは parseAst にあり、AST ツリーを生成し、対応するコンテンツを走査して処理します。
//taro-cli-convertor/src/index.ts
parseAst ({ ast, sourceFilePath, outputFilePath, importStylePath, depComponents, imports = [] }: IParseAstOptions): {
ast: t.File
scriptFiles: Set<string>
} {
...
// 转换后js页面的所有自定义标签
const scriptComponents: string[] = []
...
traverse(ast, {
Program: {
enter (astPath) {
astPath.traverse({
//对类的遍历和判断
ClassDeclaration (astPath){...},
//表达式
ClassExpression (astPath) {...},
//导出
ExportDefaultDeclaration (astPath) {...},
//导入
ImportDeclaration (astPath) {...},
//调用
CallExpression (astPath) {...},
//检查节点的 object 属性是否为标识符 wx,如果是,则将 object 修改为标识符 Taro,并设置一个标志变量 needInsertImportTaro 为 true。这段代码可能是将 wx 替换为 Taro,以实现对 Taro 框架的兼容性处理。
MemberExpression (astPath) {...},
//检查节点的 property 属性是否为标识符 dataset,如果是,则将 object 修改为一个 getTarget 函数的调用表达式,传递了两个参数 object 和标识符 Taro。它还创建了一个导入语句,将 getTarget 函数引入,并将其赋值给一个对象模式。这段代码可能是对可选链式调用中的 dataset 属性进行处理,引入了 getTarget 函数来实现相应的转换。
OptionalMemberExpression (astPath) {...},
// 获取js界面所有用到的自定义标签,不重复
JSXElement (astPath) {...},
// 处理this.data.xx = XXX 的情况,因为此表达式在taro暂不支持, 转为setData
// 将this.data.xx=XX 替换为 setData()
AssignmentExpression (astPath) {...}
})
},
exit (astPath) {...}
},
})
...
return {
ast,
scriptFiles,
}
}
ps: 公式ツールでは逆変換が提供されていますが、それでも制限があります。すべてのミニ プログラムが逆変換をサポートしているわけではありません。現在、逆変換をサポートしているのは WeChat ミニ プログラムのみです。また、すべてのネイティブ API を変換できるわけではないため、注意が必要です。今後もこの機能の拡張・改善が期待されます。
パフォーマンスの最適化 - プリレンダリング
なぜ Prerender が必要なのでしょうか? 公式の説明は次のとおりです。
Taro Next は、ページを読み込むときに次の手順を実行する必要があります。
フレームワーク (React/Nerv/Vue) がページを仮想 DOM にレンダリングします。
Taro を実行すると、ページの仮想 DOM をレンダリング可能なデータにシリアル化し、setData() を使用してページのレンダリングを実行します。
アプレット自体がシリアル化されたデータをレンダリングします
ネイティブ アプレットまたはコンパイルされたアプレット フレームワークと比較すると、手順 1 と 2 は冗長です。ページのビジネス ロジック コードにパフォーマンスの問題がない場合、パフォーマンスのボトルネックのほとんどはステップ 2 の setData() にあります。最初のレンダリングはページの仮想 DOM ツリー全体であるため、データ量が比較的多くなります。したがって、setData() は比較を渡す必要があります。データが大きいと、ページの初期化時に白い画面が表示される期間が発生します。この状況は通常、ページの初期レンダリング用の wxml ノードの数が比較的大きい場合、またはユーザーのマシンのパフォーマンスが低い場合に発生します。
Taro プリレンダリングの動作原理は、構築フェーズ中にサーバー側レンダリング (SSR) テクノロジーを使用して、ページ コンポーネントを静的 HTML ファイルにレンダリングし、静的ファイル ディレクトリに保存することです。その後、クライアントがページをリクエストすると、ページを動的に生成する代わりに、事前にレンダリングされた静的 HTML が直接返されます。
構築段階でページを静的 HTML ファイルとしてレンダリングすることで、最初の読み込み速度が向上し、ユーザー エクスペリエンスが向上し、検索エンジンのインデックス作成が最適化されます。
使い方:
//config/index.js 或 /config/dev.js 或 /config/prod.js
const config = {
...
mini: {
prerender: {
match: 'pages/shop/**', // 所有以 `pages/shop/` 开头的页面都参与 prerender
include: ['pages/any/way/index'], // `pages/any/way/index` 也会参与 prerender
exclude: ['pages/shop/index/index'] // `pages/shop/index/index` 不用参与 prerender
}
}
};
module.exports = config
使用方法の詳細については、公式 Web サイトのドキュメントを参照してください。
要約する
上記の表面的な分析を経て、Taro の全体的な動作メカニズムを予備的に理解することができます。仕組みの概要は次のとおりです。
-
コード変換と条件付きコンパイル: Taro は、ソース コードにコード変換と条件付きコンパイルを適用することにより、ターゲット プラットフォームに適したコードを生成します。これにより、1 セットのコードを使用して複数のプラットフォーム用のアプリケーションを作成できるようになります。
-
抽象化層とプラットフォーム アダプテーション層: Taro は、コード変換プロセスを処理し、さまざまなプラットフォームでの API の互換性を確保するために、抽象化層とプラットフォーム アダプテーション層を提供します。これにより、同じ API を使用して異なるプラットフォームで開発できるようになります。
-
Taro のカスタム コンポーネントとマルチターミナルの適応性: Taro の組み込みコンポーネントはフレームワークに自然に適応します。つまり、Taro UI などの複数のプラットフォームに適したコンポーネント ライブラリを構築できます。これにより、開発効率が向上し、クロスプラットフォームの一貫性が可能になります。
-
逆変換: 逆変換は、既存のアプリケーションを Taro コードに変換することでクロスプラットフォームを実現しようとする逆の考え方です。ただし、逆変換には不安定性と制限があり、保守者にとってのメリットは限られています。
-
パフォーマンス最適化オプションとしてのプリレンダリング: Taro は、パフォーマンス最適化オプションとしてプリレンダリング テクノロジを提供します。プリレンダリングにより、構築プロセス中に静的 HTML ページが生成され、初回の読み込み速度が向上し、検索エンジンのインデックス作成が最適化されます。これはパフォーマンスを最適化する効果的な手段です。