React Native 0.79 - ツールの高速化など
本日、React Native 0.79のリリースを発表できることを嬉しく思います!
このリリースには、様々な面でのパフォーマンス改善と、いくつかのバグ修正が含まれています。まず、Metroはハッシュ化の遅延実行により起動が高速化され、package exportsの安定サポートが追加されました。また、Androidでの起動時間も、JSバンドルの圧縮方法の変更などにより改善されます。
ハイライト
ハイライト
Metro: 起動の高速化とpackage exportsのサポート
このリリースにはMetro 0.82が含まれています。このバージョンでは、ハッシュ化の遅延実行(deferred hashing)を利用して、初回の `yarn start` の速度を通常3倍以上(大規模プロジェクトやモノレポではさらに)向上させ、日々の開発体験やCIビルドを高速化します。
また、Metro 0.82では、`package.json` の `"exports"` と `"imports"` フィールドの解決が安定版に昇格しました。`"exports"` の解決はReact Native 0.72で導入され、`"imports"` のサポートはコミュニティのコントリビューションによって追加されました。これらは両方とも、React Native 0.79のすべてのプロジェクトでデフォルトで有効になります。
これにより、最新のnpm依存関係との互換性が向上し、プロジェクトを整理するための新しい、標準に準拠した方法が利用可能になります。
`package.json` の `"exports"` についてはコミュニティでしばらくテストしてきましたが、この切り替えは特定のパッケージやプロジェクトのセットアップにとって破壊的変更となる可能性があります。
特に、FirebaseやAWS Amplifyなど、いくつかの人気パッケージでユーザーから互換性のない問題が報告されており、これらを根本的に修正するよう取り組んでいます。
問題が発生した場合
- Metro 0.81.5 hotfixにアップデートするか、オプトアウトするために`resolver.unstable_enablePackageExports = false`を設定してください。
- 影響を受けるパッケージと今後の更新については、expo/expo#36551を参照してください。
JSCがコミュニティパッケージへ移行
React NativeのAPIサーフェスを縮小する取り組みの一環として、JavaScriptCore(JSC)エンジンをコミュニティがメンテナンスするパッケージである`@react-native-community/javascriptcore`に移行するプロセスを進めています。
この変更は、Hermesを使用しているユーザーには影響しません。
React Native 0.79からは、readmeのインストール手順に従うことで、コミュニティがサポートするバージョンのJSCを使用できます。React Nativeコアが提供するJSCバージョンは0.79でも引き続き利用可能ですが、近い将来に削除する予定です。
JSCをコミュニティがメンテナンスするパッケージに移行することで、JSCのバージョンをより頻繁に更新し、最新の機能を提供できるようになります。コミュニティがメンテナンスするJSCは、React Nativeとは別のリリーススケジュールに従います。
iOS: Swift互換のネイティブモジュール登録
このリリースでは、ネイティブモジュールをReact Nativeランタイムに登録する方法を刷新しています。この新しいアプローチは、公式ドキュメントで説明されているコンポーネントと同じアプローチに従います。
このバージョンのReact Nativeからは、`package.json` ファイルを修正することでモジュールを登録できます。`ios` プロパティに新しい `modulesProvider` フィールドを導入しました。
"codegenConfig": {
"ios": {
+ "modulesProvider": {
+ "JS Name for the module": "ObjC Module provider for the pure C++ TM or a class conforming to RCTTurboModule"
+ }
}
}
Codegenは、`package.json` ファイルから関連するすべてのコードを生成します。
純粋なC++ネイティブモジュールを使用している場合は、以下の推奨設定に従う必要があります。
アプリで純粋なC++ネイティブモジュールを設定する
純粋なC++ネイティブモジュールの場合、C++ネイティブモジュールとアプリの他の部分を繋ぐために、新しいObjective-C++クラスを追加する必要があります。
#import <Foundation/Foundation.h>
#import <ReactCommon/RCTTurboModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface <YourNativeModule>Provider : NSObject <RCTModuleProvider>
@end
NS_ASSUME_NONNULL_END
#import "<YourNativeModule>Provider.h"
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/TurboModule.h>
#import "<YourNativeModule>.h"
@implementation NativeSampleModuleProvider
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeSampleModule>(params.jsInvoker);
}
この新しいアプローチにより、アプリ開発者とライブラリメンテナーの両方にとって、ネイティブモジュールの登録方法が統一されました。ライブラリは、`package.json` に同じプロパティを指定でき、残りはCodegenが処理します。
このアプローチは、0.77で導入された、Swiftの `AppDelegate` を使用している場合に純粋なC++ネイティブモジュールを登録できないという制限を解決します。ご覧の通り、これらの変更は `AppDelegate` を変更せず、生成されたコードはSwiftとObjective-Cの両方で実装された `AppDelegate` で動作します。
Android: アプリ起動の高速化
Androidの起動時間を大幅に改善する変更も提供しています。
このバージョンから、APK内のJavaScriptバンドルを圧縮しなくなります。以前は、アプリが起動する前にAndroidシステムがJavaScriptバンドルを解凍する必要がありました。これがアプリの起動時に大幅な遅延を引き起こしていました。
このリリースからは、JavaScriptバンドルをデフォルトで非圧縮で提供するため、Androidアプリの起動が全体的に速くなります。
Margeloチームがこの機能をDiscordアプリでテストしたところ、大幅なパフォーマンス向上が得られました。Discordのインタラクティブになるまでの時間(TTI)が400ms短縮され、これは1行の変更で12%の高速化でした(Samsung A14でテスト)。
一方で、バンドルを非圧縮で保存すると、ユーザーのデバイス上でアプリケーションがより多くのスペースを消費することになります。これが懸念される場合は、`app/build.gradle` ファイルの `enableBundleCompression` プロパティを使用してこの動作を切り替えることができます。
react {
// ...
// If you want to compress the JS bundle (slower startup, less
// space consumption)
enableBundleCompression = true
// If don't you want to compress the JS bundle (faster startup,
// higher space consumption)
enableBundleCompression = false
// Default is `false`
}
このリリースではAPKサイズは増加しますが、APKはネットワークからダウンロードされる際に圧縮されるため、ユーザーがAPKダウンロードサイズで余分なコストを支払うことはありません。
破壊的変更
リモートJSデバッグの削除
デバッグ改善への継続的な取り組みの一環として、Chromeを介したリモートJSデバッグを削除します。このレガシーなデバッグ方法は、React Native 0.73で非推奨となり、ランタイムでのオプトインに移行していました。モダンで信頼性の高いデバッグにはReact Native DevToolsを使用してください。
これはまた、React Nativeがコミュニティプロジェクトであるreact-native-debuggerと互換性がなくなることを意味します。Redux DevToolsなどのサードパーティ製デバッグ拡張機能を使用したい開発者には、Expo DevTools Pluginsを使用するか、これらのツールのスタンドアロン版を統合することをお勧めします。
詳細については、こちらの専用投稿をお読みください。
内部モジュールが `export` 構文に更新
JavaScriptコードベースの近代化の一環として、`react-native` 内の多くの実装モジュールを、`module.exports` の代わりに一貫して `export` 構文を使用するように更新しました。
合計で約**46のAPI**を更新しました。これらは変更履歴で確認できます。
この変更は、既存のインポートに微妙な影響を与えます。
ケース1:デフォルトエクスポート
// CHANGED - require() syntax
- const ImageBackground = require('react-native/Libraries/Image/ImageBackground');
+ const ImageBackground = require('react-native/Libraries/Image/ImageBackground').default;
// Unchanged - import syntax
import ImageBackground from 'react-native/Libraries/Image/ImageBackground';
// RECOMMENDED - root import
import {ImageBackground} from 'react-native';
ケース2:セカンダリエクスポート
このパターンのケースは非常に少なく、ルートの `'react-native'` インポートを使用している場合は影響を受けません。
// Unchanged - require() syntax
const BlobRegistry = require('react-native/Libraries/Blob/BlobRegistry');
// Unchanged - require() syntax with destructuring
const {register, unregister} = require('react-native/Libraries/Blob/BlobRegistry');
// CHANGED - import syntax as single object
- import BlobRegistry from 'react-native/Libraries/Blob/BlobRegistry';
+ import * as BlobRegistry from 'react-native/Libraries/Blob/BlobRegistry';
// Unchanged - import syntax with destructuring
import {register, unregister} from 'react-native/Libraries/Blob/BlobRegistry';
// RECOMMENDED - root import
import {BlobRegistry} from 'react-native';
この変更の影響は、特にTypeScriptで書かれ、`import` 構文を使用しているプロジェクトでは非常に限定的であると予想しています。コードを更新するために、型エラーがないか確認してください。
ルートの `react-native` インポートを強く推奨します
一般的な教訓として、将来の余計な破壊的変更を避けるため、ルートの `'react-native'` パスからインポートすることを強くお勧めします。次のリリースでは、React Nativeの公開JavaScript APIをより明確に定義する一環として、ディープインポートを非推奨にする予定です(RFCを参照)。
その他の破壊的変更
このリストには、製品コードに軽微な影響を与える可能性があり、注目に値すると思われる他の破壊的変更が含まれています。
- box shadowとfilterにおける無効な単位なしの長さ:
- React NativeをCSS/Web仕様にさらに準拠させるため、`box-shadow` と `filter` で単位のない長さのサポートを終了しました。これは、`box-shadow` を `1 1 black` のように使用していた場合、レンダリングされなくなることを意味します。代わりに `1px 1px black` のように単位を指定する必要があります。
- normalize-colorから不正なhwb()構文のサポートを削除
- React NativeをCSS/Web仕様にさらに準拠させるため、`hwb()` の一部の無効な構文を制限しました。これまでReact Nativeはカンマ区切りの値(例: `hwb(0, 0%, 100%)`)をサポートしていましたが、これをサポートしなくなりました(`hwb(0 0% 100%)` に移行する必要があります)。この変更の詳細についてはこちらをご覧ください。
- Libraries/Core/ExceptionsManagerのエクスポートの更新
- React Native JS APIを近代化する取り組みの一環として、`ExceptionsManager`を更新し、デフォルトで`ExceptionsManager`オブジェクトをエクスポートし、セカンダリエクスポートとして`SyntheticError`をエクスポートするようにしました。
謝辞
React Native 0.79には、100人のコントリビューターによる944以上のコミットが含まれています。皆さんの多大な貢献に感謝します!
このリリースで重要な貢献をしてくださったコミュニティメンバーの方々に感謝の意を表します。
- 「Android: アプリ起動の高速化」機能の開発とドキュメント化を行ったMarc Rousavy氏
- `@react-native-community/javascriptcore` パッケージに取り組み、「JSCがコミュニティパッケージへ移行」セクションを執筆したKudo Chien氏とOskar Kwaśniewski氏
- Metroにインポートサブパス解決のサポートを追加したJames Lawson氏
さらに、このリリース記事で機能のドキュメント化に取り組んでくださった以下の著者の方々にも感謝します。
- 「新しいMetroの機能」セクションを担当したRob Hogan氏
- 「リモートJSデバッグの削除」と「内部モジュールが `export` 構文に更新」セクションを担当したAlex Hunt氏
- iOSネイティブモジュール登録に取り組んだRiccardo Cipolleschi氏
0.79へのアップグレード
既存のプロジェクトについては、アップグレードドキュメントに加えて、React Native Upgrade Helperを使用して、React Nativeのバージョン間のコード変更を確認してください。
新しいプロジェクトを作成するには
npx @react-native-community/cli@latest init MyProject --version latest
Expoを使用している場合、React Native 0.79は、今後のExpo SDK 53でデフォルトのReact Nativeバージョンとしてサポートされる予定です。
0.79は現在、React Nativeの最新の安定版であり、0.76.xはサポート対象外に移行します。詳細については、React Nativeのサポートポリシーを参照してください。近い将来、0.76の最終的なサポート終了アップデートを公開する予定です。