導入#
本文は、クロスエンドフレームワーク技術のテーマに基づき、その技術目標と 3 つの進化方向を分析し、業界の自描画クロスエンドソリューションの技術実装を明らかにします ——Kun、WebF、TDF、Weex 2.0、Waft、MiniX などのソリューションを含み、それぞれの特徴と欠点を分析し、クロスエンドフレームワークの研究開発の考え方と技術的要点をまとめ、最終的にクロスエンドフレームワークの発展動向についての考察を共有します。
共有の過程では、クロスエンドフレームワークのスクリプトエンジンの選定と技術的難点、業界の各クロスエンドフレームワークのそれぞれの困難、デバッガーの原理、そして一核多エコシステムのエンジニアリング思考についても紹介します。
アウトラインは以下の通りです:
- クロスエンドフレームワークの技術目標(略)
- クロスエンドフレームワークの技術方向
- クロスエンドフレームワークの技術の秘密
- クロスエンドフレームワークの技術要点
- クロスエンドフレームワークの発展動向(略)
注 1:この記事はオフライン共有の文字要約版であり、前提の要約、技術背景(第 1 節、第 5 節など)および一部技術詳細の拡張(第 3-4 節の一部内容)を省略し、核心内容のみを保持します。
注 2:この記事の資料は、GMTC 会議のクロスエンドテーマの公開共有、一部企業の公開 WeChat 公式アカウントの記事、フレームワークの公開ソースコードおよび個人の歴史的共有素材に基づいており、その他の内部共有および内部フレームワークなどの資料はデータを脱敏処理しています。
クロスエンドフレームワークの技術方向#
私は少しまとめましたが、クロスエンドフレームワークには以下の 4 つの技術方向があります。
方向 1:WebView ベースの強化#
WebView ベースの強化は、フロントエンドからクライアント方向に寄せられた技術方向であり、上層のエコシステムは依然として Web 技術を使用しますが、クライアントが WebView に対していくつかの能力を補完する必要があります。
例えば:
- Ionic、Cordova などのハイブリッドフレームワーク。
- 業界の多くの WebView ベースのミニプログラム。
- Sonic などのクライアントが WebView リソースを事前読み込みするソリューション。
- アプリメーカーが Web 向けに提供するオフラインパッケージソリューション。
この種のフレームワークには以下の特徴があります:
- WebView レンダリングに基づいているが、いくつかのネイティブ能力を補強している。
- 開発エコシステムは Web フロントエンドエコシステムに基づいている(厳密には、ミニプログラムもそうです)。
- Web のユーザーエクスペリエンスを向上させるためにあらゆる手段を講じている。
方向 2:DSL ベースのネイティブ強化#
DSL ベースのネイティブ強化は、クライアント寄りですがフロントエンド方向に寄せられた技術方向であり、開発エコシステムはネイティブに基づいていますが、フレームワーク設計は WebView のいくつかの特性を参考にしています。これらのフレームワークは全体的に DSL をカスタマイズしてクロスエンドと動的化を実現しています。
例えば、手淘のアクセシビリティフレームワーク DinamicX、美団の MTFlexbox、アリババのTangram(七巧板)。
これらのフレームワークのドキュメントを閲覧すると、設計が React Native に似ていることがわかります。ただし、上層では開発者が DSL を使用してコンポーネントに接続する必要があります。
方向 3:コード共有#
コード共有は、端末の開発ソリューションであり、業界でよく知られているソリューションは KMM(Kotlin Multiplatform Mobile)です。K2 コンパイラを通じて Kotlin ソースコードを各プラットフォームのターゲットコードにコンパイルし、クロスエンドを実現します。具体的には、Kotlin はコンパイラのフロントエンドを通じて意味情報を持つ FIR を生成し、その後 FIR は各プラットフォームのコンパイラのバックエンドに渡されて最適化と生成が行われます。最終的に各プラットフォームで実行可能なターゲットコードが生成されます。
KMM のエンジニアリング構造は比較的シンプルで、クロスエンドコード(Shared module)とラッパーエンジニアリング(Android/iOS App)の 2 つの部分で構成されています。
現在、このソリューションは iOS コードを生成する際にマルチスレッドのロジック処理があまり良くなく、ビジネス側の最適化が必要です。
方向 4:GPL ベースのネイティブ強化#
GPL ベースのネイティブ強化は、大端末の開発ソリューションと見なしています。大端末とは、融合した後の産物であり、PC 時代には.NET、JVM、Qt を使用してデスクトップアプリケーションを開発していました。これを端末開発と呼びます。その後、モバイル端末時代に入り、ネイティブ方向のクライアント開発は動的化の道を追求し続け、クロスプラットフォーム方向のフロントエンド開発は性能の道を追求し続けました。この 2 つの道は最終的に現在のこれらのクロスエンドフレームワークに融合しました。ミニプログラム、Flutter、DSL 開発フレームワーク、WASM はいずれも融合進化の産物です。
まとめると、方向 4 には以下のようなソリューションがあります:
- ネイティブレンダリングコンポーネント:React Native / Hippy 1.0 / Weex 1.0 など。
- 自描画エンジン:Flutter。暫定的にこれを単独でカウントします。
- Flutter ベースの自描画フレームワーク:業界には多くのフレームワーク(20 以上のフレームワークを整理したことがあります)があり、WebF(Kraken)、Kun、FMP、Skyline ベースのミニプログラムなどがあります。
- システムグラフィックスライブラリ(Skia / Vulkan / Metal / OpenGL)ベースの自描画フレームワーク:業界にも多くのフレームワークがあり、TDF、Hippy 3.0、Weex 2.0、Waft などがあります。厳密には Flutter もこのカテゴリに属します。
前の 3 つの方向と第 4 の方向の前の 2 つのタイプのフレームワークはすべてオープンソースであり、業界にもそれらの原理を紹介した多くの記事がありますので、ここでは詳しく述べません。この記事では主に第 4 の方向の後の 2 つのタイプのフレームワークの技術ソリューションについて紹介します。
クロスエンドフレームワークの技術の秘密#
このセクションでは、いくつかの代表的なフレームワークを選んで技術の秘密を明らかにします:
- Kun
- WebF
- Weex 2.0
- TDF(脱敏が必要、略)
- Waft
- MiniX(略)
数多くのフレームワークの中からこれらを選んだ理由は、彼らのソリューションが分野の詳細において典型的なフレームワークであり、これらのフレームワークのアプリケーション開発システム、スクリプトエンジンとレンダリングエンジンの選定に注目できるからです。
Kun#
Kun は、闲鱼が Flutter を基に開発したクロスエンドフレームワークで、現在はオープンソースではありません。オンラインで学べる記事は、闲鱼の公式アカウントに掲載された 3 つの記事だけです。アーキテクチャ設計は比較的シンプルで、ソースコードがなくても分析できます。アーキテクチャ図はここには掲載しませんが、興味のある方は記事をクリックして確認してください。
Kun の全体的な設計思想は、Flutter を基に JS ランタイムを開発することで、開発者はフロントエンドエコシステムを使用してページを開発し、JS インタープリターが接着剤の役割を果たし、ソースコードを Flutter Widget に翻訳し、その後 Flutter Engine に渡してレンダリングします。
JS エンジンには QuickJS を採用していますが、推測するにアリババ内部の QKing エンジン(QuickJS に基づく)であると思われます。
デバッガーは CDP をサポートし、テストは Flutter Golden テストに基づいています。
CSS 解析では、最初に Yoga を使用してポリフィルを行い、スタイルを css in js に処理し、その後解析モジュールは Kraken の遺産 CSSLib を流用し、Dart FFI を通じて JS 測定のインラインスタイルを Dart 側に渡して処理し、最終的に Flutter Widget に解析されます。
しかし、CSS のボックスモデルとドキュメントフローは Flutter Widget のスタイル標準とは大きく異なるため、彼らは Widget の接続方式を採用し、各レイヤーの Widget が特定のスタイルを処理し、最終的に層を重ねて接続する方式でコンポーネントスタイルを実現しています。以下の図は、div 要素に対応する接続方式です:
特徴をまとめると:
- 完全な W3C 標準をサポートしていない(サポートすることは不可能であり、例えば css in js は擬似クラスを実現できない)、各標準のサブセットのみをサポートしています。これには HTML タグ、CSS スタイルセット、WebAPI 標準が含まれます。
- Dart 側で実装されたカスタム Element コンポーネントを提供しており、ビジネス側も Dart 側を使用してカスタム Element を開発できます。
- コンポーネントの実装には Widget の接続方式を採用しています。
このセクションの参考資料:
- 大端末分野の新しい生物 - KUN
- 三代端末コンテナ KUN の初の大試験【アーキテクチャの進化】
- ダブルイレブン|KUN の読み込み性能と強化体験を探る
WebF#
WebFの前身はアリババの Kraken で、後にチームが解散し、一部の遺産が Kun に引き継がれ、残りのメンバーがオープンソースコミュニティで openwebf を設立し、Kraken を WebF に改名して維持しています。
これが WebF のアーキテクチャ図です:
Kun とは異なる点は、JSBinding を提供するだけでなく、チームが Flutter の Dart 側でいくつかの開発を行い、RenderObject の能力を豊かにして W3C 標準に適応させたことです —— つまり、Dart 層で CSS を実現し、C++ 層で WebAPI を実現し、W3C 標準に対応しています。
スクリプトエンジンは依然として QuickJS ですが、現在は最適化が行われており、学ぶ価値があります。
実際、Kun と WebF を比較すると、CSS の処理に対して 2 つの異なるアプローチを採用していることがわかります。
まず Kun についてですが、そのソリューションにはいくつかの問題があります:
- レンダリングチェーンに 2 回のレイアウトが存在するのは完全に不必要であり、レイアウトの更新頻度自体も非常に高いため、2 回のレイアウトは追加のパフォーマンスオーバーヘッドをもたらします。
- Dart FFI はスタイル更新の情報伝達を支えるには不十分であり、スタイル更新のデータ量が非常に大きく、FFI のボトルネックに触れることになります。
- インラインスタイルの開発体験が悪く、多くの CSS プロパティも実現できません。
では、CSS はどのように実現すべきでしょうか?2 つの良い解決策があります:
- CSS を Dart 層で実現し、スタイル更新は RenderObject のレイアウトに依存し、FFI を経由する必要はありません。
- DOM と CSS をすべて C++ で実現し、Dart 層を剥離します。
解決策 1 が WebF であり、解決策 2 は後述の Weex 2.0 や TDF などのフレームワークです。
しかし、解決策 1 にも技術的な難点があります。CSS を導入すると RenderObject ツリーの管理が難しくなるため、RenderObject ツリーをどのように管理すべきでしょうか?これにも 2 つのアプローチがあります:
- RenderObject を薄くする:つまり、Flutter Widget を原子レベルのレンダリングコンポーネントとし、RenderObject を変更せず、上層が RenderObject を組み合わせて複雑な機能とスタイルを実現します。Kun のように。
- RenderObject を厚くする:大量のレイアウトレンダリング能力を統合し、上層がスタイルシートで RenderObject のレンダリングを駆動します。
明らかに、RenderObject を厚くする方がより良いソリューションです。なぜなら、前者は複雑すぎるからです(前述の層層嵌套のコードを見ても直感的に感じられます)。各スタイルルールの計算は一層一層確認推論する必要があり、メンテナンス効率が低下します。
したがって、私は WebF のソリューションに期待を寄せており、WebF は現在の多くのクロスエンドフレームワークの中で唯一オープンソースを受け入れているソリューションでもあります。興味のある方はTSCに参加して共に構築しましょう。
このセクションの参考資料:
- https://github.com/openwebf/webf
- 晟怀:《WebF はどのように高性能で Flutter + Web の融合を実現したのか》(2022 QCon)
Weex 2.0#
Weex 2.0 はアリババ内部でオープンソースされたクロスエンドソリューションであり、現在ほぼアリババ内部の一核多エコシステムを実現しています。技術アーキテクチャは 1.0 を完全に再構築し、その間に多くの探索の道を歩んできました。共有から見ると、全体のソリューションは比較的完備しており、作業量も非常に大きいです。
これが Weex 2.0 の構造図です:
以下のコンポーネントについて重点的に紹介します:
- WeexAbility:コンテナと能力の拡張、URL のインターセプト、キャッシュ、基本 API、サードパーティの拡張など。
- WeexFramework:汎用基盤フレームワーク。ページインスタンスをカプセル化し、DOM、CSSOM、WebAPI 標準を実現し、スクリプトエンジンとレンダリングエンジンを分離します。
- QKing:スクリプトエンジン、QuickJS を基にした魔改造。
- Unicorn:自描画レンダリングエンジン。CSS 能力を実現し、完全なノード構築、アニメーション、ジェスチャー、レイアウト、描画、合成、ラスタライズレンダリングパイプラインを含み、クロスプラットフォームで使用可能です。
- WeexUIKit:ネイティブ UI レンダリングエンジンで、ネイティブコンポーネントをカプセル化しています。
2.0 のソースコードは、前のいくつかのフレームワークと同様に jsbundle から生成されたバイトコードですが、コンパイル時にいくつかの SSA の最適化が行われています。さらに、JS ランタイムも多くの最適化が行われており、全体のリンクは C++ で開発されており、追加の通信オーバーヘッドがなく、冗長な抽象がなく、リンクが短くなっています。同時に、自社開発の Unicorn に基づいており、精緻なレイアウトアルゴリズム、精密な操作ジェスチャーとアニメーションを持ち、システムグラフィックスライブラリに直通しています。全体のソリューションは 1.0 とは無関係であり、1.0 のクロス言語通信問題、両端レンダリングの差異問題、レイアウトアルゴリズム問題、スクリプト実行効率問題を解決しました。
Weex 2.0 に基づいて、アリババは煙突式ソリューションの問題を解決し、多核同構のコアに基づいて基盤能力の統一を推進し、これにより差別化されたビジネスシーンをサポートしています:
このセクションの参考資料:
- 门柳:《淘宝新一代自描画レンダリングエンジンのアーキテクチャと実践》(2023 GMTC)
注:テンセントの TDF も類似の作業に取り組んでおり、ここでは脱敏処理のために紹介を省略します。
Waft#
Waft は WebAssembly Framework for Things の略で、天猫精霊チームが WebAssembly ランタイムと Skia を基に開発した自描画フレームワークで、オープンソースではありません。現在のところ、フレームワークは実現されていませんが、AIoT のシーンをサポートしています。しかし、原理的にはクロスエンドが可能であるため、ここで紹介して思考を広げます。
天猫精霊は初期に AIoT に関していくつかの試みを行い、最初は Android アプリを開発しましたが、運存が非常に低く、数百 MB しかなく、性能が制限されました。その後、彼らはクラウドアプリケーションを開発しましたが、効果はまあまあでしたが、サーバーコストが非常に高く、停止されました。そこで、彼らは端末レンダリングの道を探求し、Waft を開発しました。
これが Waft のアーキテクチャ図です:
彼らはまた、読み込みプロセスとレンダリングプロセスを再設計しました:
全体的に作業量が非常に大きく、フロントエンドの標準とエコシステムには適合していません。
ここで、スクリプトエンジンの選定に関して WebAssembly の比較図を提供しています:
このスクリプトエンジンの選定には疑問があります。考えてみると、以下の不足があります(彼らの内部には他の考慮があるかもしれません):
- fib のユースケースが非常に単純で、JS エンジンの利点を十分に発揮できません。
- AOT と比較して解釈実行は明らかに不公平です。
- QuickJS は原始的なバージョンを使用しているはずで、まだ大きな最適化の余地があります。
- JIT モードのエンジン、例えば V8 や JSCore などとの比較が行われていません。
- どの wasm フレームワークを使用しているのかが明示されていません。なぜなら、異なる wasm の実装の性能表現は異なり、解釈実行の効率を重視するものもあれば、AOT/JIT の効率を重視するものもあるからです。
Waft 自体にも問題があり、今後の最適化を期待しています:
- CSS は一部のサブセットのみをサポートしています。
- W3C 標準(DOM Element、WebAPI)の実装が不足しています。
- パッケージサイズが大きくなる可能性があり、この部分はまだ疑問です。
したがって、現在の Waft の実装はアプリケーションシーンを決定し、単純な IoT ページのみをサポートできる状態です。
参考資料:
- 聂鑫鑫:《Waft:WebAssembly と Skia に基づく AIoT アプリケーション開発フレームワーク》(2023 GMTC)
クロスエンドフレームワークの技術要点#
動的化#
上記のフレームワークを紹介したことで、クロスエンドフレームワークのアプリケーションシーンをまとめることができます:
- 動的化
- IoT
- デスクトップ
- 車載機器
- 一核多エコシステム
「動的化能力のないクロスエンド技術は魂がない」と言われるように、実際、動的化フレームワークとクロスエンドフレームワークの多くの部分は完全に重なっています。以前、私は動的化の 5 つの実現思考をまとめたことがあります:
- WebView ベースの強化
- DSL ベースのネイティブ強化
- GPL ベースのネイティブ強化
- プラグイン化(Android)
- OC ランタイムの動的化特性を利用(iOS)
私は補足説明のために図を描きました:
注:この図は私が比較的早く描いたもので、実際には左上隅を「Flutter と他の自描画フレームワーク」に変更できます。
彼らの核心は、実行可能なコードをランタイム中に読み込み、呼び出すことです。最初の 3 つの動的化の思考と私たちがまとめたクロスエンドフレームワークの技術方向は全く同じです。
技術的要点は以下のように考えています:
- スクリプトエンジン
- レンダリングエンジン
- デバッガー
- エンジニアリング
それぞれについて紹介します。
スクリプトエンジン#
スクリプトエンジンの選定思考には以下の 3 つがあります:
- JS エンジン:接着剤言語としてのみ使用し、JIT に強く依存しない。
- Dart VM:主に Flutter Engine を利用してレンダリングするため、Dart エコシステムを使用します。
- WARM:DSL を設計し、レンダリングエンジンを実現し、全体のエコシステムを充実させる必要があります。
JS エンジンを選択する場合、以下の選定思考があります:
- ダブルエンジンを使用:各端末が自分の強みのあるエンジンを使用し、Android は V8 を使用し、j2v8 を導入すればよく、iOS は JSCore を使用すれば完全にパッケージの増加がありません。しかし、残念ながら直接 JSCore を使用すると JIT を有効にできません。
- Hermes 単一エンジンを使用:Meta が React Native のようなハイブリッドフレームワークのために特別に開発したスクリプトエンジンで、すぐに使用できます。
- QuickJS 単一エンジンを使用:著名な開発者が開発した JS エンジンで、体積が非常に小さく、性能が優れています。
- 自社開発の JS エンジンを使用:基本的に業界は QuickJS を基に最適化しています。
QuickJS には現在いくつかの問題があります:
- JIT がない。これは必要に応じて実装するべきですが、JIT があれば実行効率が 1 桁向上しますが、接着剤言語として重視されるのはこれではありません。JIT はコールドスタートの時間を増加させ、メモリ使用量を増加させ、体積を増加させ、さらに iOS では使用できません。
- 手動 GC で、管理とメンテナンスが難しく、最適化の余地があります。
- 行番号記録が欠けています。
- デバッガーが欠けています。現在、GitHub にはいくつかのオープンソースプラグインが実現しています。
- コードキャッシュが欠けています。
- インラインキャッシュが欠けています。
- メモリリーク検出能力が欠けています。
- バイトコードには多くの最適化の余地があります。
レンダリングエンジン#
レンダリングエンジンの選定思考には 2 つがあります:
- Flutter Engine に基づく。
- システムグラフィックスライブラリに基づく(Skia / OpenGL / Metal / Vulkan)。
何に基づいても、フレームワークの全体的な思考はパイプラインを簡素化し、同期ラスタライズを使用することです。
デバッガー#
デバッガーは、JavaScript ランタイムを中断し、内部の実行状態をリアルタイムで確認できるアプリケーションであり、開発者が使用するツールであり、フレームワークにとって不可欠です。
現在、主に 3 つのデバッグプロトコルがあり、先ほど紹介したフレームワークは少なくともそのうちの 1 つを実装しています:
- CDP:Chrome DevTools Protocol
- DAP:Debug Adapter Protocol
- 自作プロトコル:WeChat ミニプログラムは初期に自作プロトコルを使用していました。
エンジニアリングソリューション#
エンジニアリングには少なくとも以下の作業が含まれます:
- リソース読み込みソリューション
- ダウングレード処理
- バージョン管理
- 研究開発モード
ここで以前 Q 音が開発した Kant は、エンジニアリングに関して詳細な設計と実装を行っており、ここでは詳しく述べません。
まとめ#
自描画フレームワークがよく直面する問題と解決思考:
- 開発体験が悪い:エコシステムはフロントエンドエコシステムを使用し、JS ランタイムを提供する必要があります。デバッガーを提供する必要があります。IDE は言語サービスをサポートする必要があります。
- ドキュメントが不十分:良いドキュメントを書く。
- CSS 能力が不足:標準に合わせる。Dart 側で CSS を実現する場合、RenderObject を厚くする必要があります。
- スタイルが H5 と異なる:テストケースを積み重ね、WPT で検証する。
- Android と iOS が一致しない:既存のリソースを利用し、Flutter に基づくことができます。
- コンポーネントが少なく、エコシステムがない:W3C 標準に合わせ、できるだけ完備する。
- JS 実行性能が悪い:自社開発の JS エンジンを使用する。
- 標準が不十分で、コミュニティライブラリを再利用できない:W3C 標準に合わせ、できるだけ完備する。
学ぶべき経験:
- 標準が最優先。
- 豊富なドキュメントを提供する。
- 自社開発を減らし、既存のリソースを合理的に利用する。
- 開発体験が非常に重要。
- 低端機のパフォーマンスに注目する。
参考資料と拡張読書#
- PPT(脱敏済み): https://weekly.ursb.me/slide/cross-end/
- 门柳:《淘宝新一代自描画レンダリングエンジンのアーキテクチャと実践》(2023 GMTC)
- 聂鑫鑫:《Waft:WebAssembly と Skia に基づく AIoT アプリケーション開発フレームワーク》(2023 GMTC)
- 晟怀:《WebF はどのように高性能で Flutter + Web の融合を実現したのか》(2022 QCon)
- 吉丰:《大端末分野の新しい生物 - KUN》
- openwebf/WebF
- Airing:《Kant が「QQ 音楽」での実践》(未公開)
- Airing:《Flutter の動的化ソリューション》(未公開)