跳到主要內容

優化 JavaScript 加載

解析和執行 JavaScript 代碼需要記憶體和時間。因此,隨著您的應用程式成長,延遲加載程式碼直到首次需要時通常很有用。React Native 預設帶有一些標準優化,您可以在自己的程式碼中採用一些技術,以幫助 React 更有效率地加載您的應用程式。還有一些進階的自動優化(具有自身的權衡),適用於非常大型的應用程式。

Hermes 是新 React Native 應用程式的預設引擎,並且針對高效的程式碼加載進行了高度優化。在發布版本中,JavaScript 程式碼會預先完整編譯為位元組碼。位元組碼會根據需求加載到記憶體中,並且不需要像純 JavaScript 那樣進行解析。

資訊

在此處閱讀更多關於在 React Native 中使用 Hermes 的資訊

如果一個包含大量程式碼/相依性的組件不太可能在初始渲染您的應用程式時使用,您可以使用 React 的 lazy API 來延遲載入其程式碼,直到首次渲染時才加載。通常,您應該考慮延遲載入應用程式中的螢幕級組件,以便向您的應用程式添加新螢幕不會增加其啟動時間。

資訊

在 React 的文件中,閱讀更多關於使用 Suspense 延遲載入組件的資訊,包括程式碼範例。

提示:避免模組副作用

如果您的組件模組(或其相依性)具有副作用,例如修改全域變數或訂閱組件外部的事件,則延遲載入組件可能會改變您應用程式的行為。React 應用程式中的大多數模組都不應有任何副作用。

SideEffects.tsx
import Logger from './utils/Logger';

// 🚩 🚩 🚩 Side effect! This must be executed before React can even begin to
// render the SplashScreen component, and can unexpectedly break code elsewhere
// in your app if you later decide to lazy-load SplashScreen.
global.logger = new Logger();

export function SplashScreen() {
// ...
}

進階:內聯呼叫 require

有時您可能希望延遲載入某些程式碼,直到您第一次使用它,而無需使用 lazy 或非同步 import()。您可以透過在您通常在檔案頂部使用靜態 import 的地方使用 require() 函數來做到這一點。

VeryExpensive.tsx
import {Component} from 'react';
import {Text} from 'react-native';
// ... import some very expensive modules

export default function VeryExpensive() {
// ... lots and lots of rendering logic
return <Text>Very Expensive Component</Text>;
}
Optimized.tsx
import {useCallback, useState} from 'react';
import {TouchableOpacity, View, Text} from 'react-native';
// Usually we would write a static import:
// import VeryExpensive from './VeryExpensive';

let VeryExpensive = null;

export default function Optimize() {
const [needsExpensive, setNeedsExpensive] = useState(false);
const didPress = useCallback(() => {
if (VeryExpensive == null) {
VeryExpensive = require('./VeryExpensive').default;
}

setNeedsExpensive(true);
}, []);

return (
<View style={{marginTop: 20}}>
<TouchableOpacity onPress={didPress}>
<Text>Load</Text>
</TouchableOpacity>
{needsExpensive ? <VeryExpensive /> : null}
</View>
);
}

進階:自動內聯 require 呼叫

如果您使用 React Native CLI 建置您的應用程式,require 呼叫(但不是 import)將自動為您內聯,無論是在您的程式碼中還是在您使用的任何第三方套件 (node_modules) 內部。

tsx
import {useCallback, useState} from 'react';
import {TouchableOpacity, View, Text} from 'react-native';

// This top-level require call will be evaluated lazily as part of the component below.
const VeryExpensive = require('./VeryExpensive').default;

export default function Optimize() {
const [needsExpensive, setNeedsExpensive] = useState(false);
const didPress = useCallback(() => {
setNeedsExpensive(true);
}, []);

return (
<View style={{marginTop: 20}}>
<TouchableOpacity onPress={didPress}>
<Text>Load</Text>
</TouchableOpacity>
{needsExpensive ? <VeryExpensive /> : null}
</View>
);
}
資訊

某些 React Native 框架會停用此行為。特別是,在 Expo 專案中,預設情況下不會內聯 require 呼叫。您可以透過編輯專案的 Metro 設定並在 getTransformOptions 中設定 inlineRequires: true 來啟用此優化。

內聯 require 的陷阱

內聯 require 呼叫會更改模組評估的順序,甚至可能導致某些模組永遠不會被評估。這通常可以安全地自動執行,因為 JavaScript 模組通常編寫為無副作用的。

如果您的模組之一確實有副作用 - 例如,如果它初始化某些日誌記錄機制,或修補程式碼其餘部分使用的全域 API - 那麼您可能會看到意外的行為甚至崩潰。在這些情況下,您可能希望從此優化中排除某些模組,或完全停用它。

若要停用 require 呼叫的所有自動內聯:

更新您的 metro.config.js 以將 inlineRequires 轉換器選項設定為 false

metro.config.js
module.exports = {
transformer: {
async getTransformOptions() {
return {
transform: {
inlineRequires: false,
},
};
},
},
};

若要僅require 內聯中排除某些模組:

有兩個相關的轉換器選項:inlineRequires.blockListnonInlinedRequires。請參閱程式碼片段,以取得如何使用每個選項的範例。

metro.config.js
module.exports = {
transformer: {
async getTransformOptions() {
return {
transform: {
inlineRequires: {
blockList: {
// require() calls in `DoNotInlineHere.js` will not be inlined.
[require.resolve('./src/DoNotInlineHere.js')]: true,

// require() calls anywhere else will be inlined, unless they
// match any entry nonInlinedRequires (see below).
},
},
nonInlinedRequires: [
// require('react') calls will not be inlined anywhere
'react',
],
},
};
},
},
};

請參閱 Metro 中 getTransformOptions 的文件,以取得更多關於設定和微調您的內聯 require 的詳細資訊。

進階:使用隨機存取模組捆綁包 (非 Hermes)

資訊

使用 Hermes 時不支援。 Hermes 位元組碼與 RAM 捆綁包格式不相容,並且在所有使用案例中提供相同(或更好)的效能。

隨機存取模組捆綁包(也稱為 RAM 捆綁包)與上述技術結合使用,以限制需要解析和加載到記憶體中的 JavaScript 程式碼量。每個模組都儲存為單獨的字串(或檔案),僅在需要執行模組時才解析。

RAM 捆綁包可能會在物理上拆分為單獨的文件,或者它們可以使用索引格式,包含單一檔案中多個模組的查找表。

在 Android 上,透過編輯您的 android/app/build.gradle 檔案來啟用 RAM 格式。在 apply from: "../../node_modules/react-native/react.gradle" 行之前,添加或修改 project.ext.react 區塊

project.ext.react = [
bundleCommand: "ram-bundle",
]

如果您想在 Android 上使用單一索引檔案,請使用以下程式碼行

project.ext.react = [
bundleCommand: "ram-bundle",
extraPackagerArgs: ["--indexed-ram-bundle"]
]

請參閱 Metro 中 getTransformOptions 的文件,以取得更多關於設定和微調您的 RAM 捆綁包建置的詳細資訊。