メインコンテンツへ移動

React Native アーキテクチャの進化

無料2019-09-14#Tool#React Native new Architecture#React Native 新架构#React Native Fabric#React Native 重构#React Native rearchitect

React Native の最初の設計にはどのような問題があり、どのように改善する予定でしょうか?

はじめに

前篇(React Native アーキテクチャ一覧)では設計、スレッドモデルなどの方面から React Native の既存アーキテクチャを紹介しました。本稿ではこのアーキテクチャの限界と、React Native が進行中のアーキテクチャアップグレード計画を分析します

一.既存アーキテクチャの限界

最初の設計はいくつかの制限ももたらしました:

  • 非同期:JavaScript ロジックを同期回答を必要とする多くの Native API と直接統合できません

  • バッチ処理:React Native アプリケーションに Native 実装の関数を呼び出させるのが困難です

  • 直列化:メモリを直接共有するのではなく、不要なコピーが存在します

これらの問題は Native + React Native のハイブリッドアプリケーションで特に顕著です:

For apps that are entirely built in React Native, these restrictions are usually bearable. But for apps with complex integration between React Native and existing app code, they are frustrating.

二.アーキテクチャアップグレード計画

そのため、2018 年 6 月に大規模リファクタリングの計画が提案され、ハイブリッドアプリケーションをより良くサポートすることを目的としています:

We're working on a large-scale rearchitecture of React Native to make the framework more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps.

具体的には、3 つの重大な変更があります:

  • スレッドモデル:任意のスレッドで JavaScript を同期呼び出しして高優先度の更新を実行可能。UI 更新はもはや 3 つのスレッドを跨ぐ必要がありません

  • React:async rendering、Data Fetching など React 16+の新機能をサポート

  • Bridge:精简最適化され、Native と JavaScript 間の直接呼び出しを可能に

同期呼び出しのサポートにより、以前は実装が困難だったものが可能になりました。例えばクロス言語のコールスタックトレースなど

アーキテクチャ図に対応すると、各層を個別に最適化するのに相当します:

  • React 層:JavaScript 型安全性を強化し、React 16+新機能をサポート

  • JavaScript 層:JSI を導入し、異なる JavaScript エンジンの置き換えを可能に

  • Bridge 層:Fabric と TurboModules の 2 部分に分割し、それぞれ UI レンダリングと Native モジュールを担当

  • Native 層:コアモジュールを精简し、非コア部分をコミュニティモジュールとして分割し独立して更新維持

初步見積もりでは、これらのリファクタリング作業は 2019 年末または 2020 年初頭に完了する予定です:

It's likely this massive piece of work will reach its conclusion around Q4 2019 or Q1 2020, but there are no confirmed dates.

P.S.現在(2019/9/8)は完了した JSI を除き、残りのリファクタリング計画は進行中です。詳細は The New React Native Architecture Explained: Part Four を参照

三.JavaScript 型安全性の強化

主な変更点は、CodeGen ツールを提供してメッセージ通信の型安全性を保証し、JavaScript と Native 通信で広く批判されている Bridge API データ型問題を解決することです:

We also experienced many issues in which the types coming from JavaScript were unexpected. For example, integers were often wrapped by strings, an issue that isn't realized until it is passed over a bridge. To make matters worse, sometimes iOS will fail silently while Android will crash.

React Native at Airbnb: The Technology より引用)

一方、型制約は通信パフォーマンスにも一定の助けがあります:

This automation will speed up the communication too, as it's not necessary to validate the data every time.

四.JSI の導入

上層 JavaScript コードはランタイム環境を必要とし、React Native ではこの環境は JSC(JavaScriptCore)です。之前とは異なり JavaScript コードを直接 JSC に入力するのではなく、新しいアーキテクチャでは JSI(JavaScript Interface)の層を導入し、JSC 上の抽象として、JavaScript エンジンの差異を隠蔽し、異なる JavaScript エンジンの使用を可能にします(最近リリースされた Hermes など)

さらに重要なのは、JSI 有了之后、JavaScript は C++ オブジェクトの参照を保持し、そのメソッドを呼び出せることです:

By using JSI, JavaScript can hold reference to C++ Host Objects and invoke methods on them.

これにより JavaScript と Native の直接呼び出しが可能になり、クロススレッドメッセージ通信を介する必要がなくなり、直列化/非直列化のコストを省き、Bridge の通信圧力を軽減できます(大量メッセージの排队渋滞など)

同時にJSI 所在の C++ 層は Native コードを再利用する方式としても機能し、Native の自然なサポートを持ちます:

  • Android:JNI(Java Native Interface)を通じて C または C++ モジュールを呼び出し

  • iOS:Objective-C はデフォルトでサポート

五.Bridge 層のリファクタリング

新しい Bridge 層は Fabric と TurboModules の 2 部分に分割されます:

  • Fabric:UI 管理を担当

  • TurboModules:Native とのインタラクションを担当

Fabric はより現代的な方式で React Native のレンダリング層を実装することを期待し、之前のレンダリングフロー中の複雑なクロススレッドインタラクション(React -> Native -> Shadow Tree -> Native UI)を簡素化します。具体的には、直接 C++ 層で JavaScript と Native が共有する Shadow Tree を作成し、JSI 層を通じて UI 操作インターフェースを JavaScript に公開し、JavaScript が高優先度の UI 操作を直接制御可能にし、同期呼び出しさえ可能にします(リストの高速スクロール、ページ切り替え、ジェスチャ処理などのシナリオに対応)

之前はすべての Native Modules(使用する必要があるかどうかに関わらず)はアプリケーション起動時に初期化が必要でした。Native は JavaScript がどの機能モジュールを呼び出すか分からないためです。新しいTurborModules は Native モジュールのオンデマンドロードを可能にし、モジュール初期化後に直接その参照を保持し、メッセージ通信に頼ってモジュール機能を呼び出す必要がなくなります。そのため、アプリケーションの起動時間も向上します

六.コアモジュールの精简

理論上、React Native は通用的で、プラットフォームに感知しないはずです。これは WebWindows など異なるプラットフォームをサポートする鍵です

Native は React Native の掌控中になく、垂直的に深く最適化できませんが、横方向の精简は可能です。非コア部分のコードをコミュニティモジュールとして分割します。例えば AsyncStorage、ImageStore、MaskedViewIOS、NetInfo など。一方ではパッケージ体積を削減し、他方ではこれらのモジュールの独立した更新維持にも有利です

参考資料

コメント

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

コメントを書く