優化 JavaScript 加載
解析和執行 JavaScript 代碼需要記憶體和時間。因此,隨著您的應用程式成長,延遲加載程式碼直到首次需要時通常很有用。React Native 預設帶有一些標準優化,您可以在自己的程式碼中採用一些技術,以幫助 React 更有效率地加載您的應用程式。還有一些進階的自動優化(具有自身的權衡),適用於非常大型的應用程式。
推薦:使用 Hermes
Hermes 是新 React Native 應用程式的預設引擎,並且針對高效的程式碼加載進行了高度優化。在發布版本中,JavaScript 程式碼會預先完整編譯為位元組碼。位元組碼會根據需求加載到記憶體中,並且不需要像純 JavaScript 那樣進行解析。
在此處閱讀更多關於在 React Native 中使用 Hermes 的資訊。
推薦:延遲載入大型組件
如果一個包含大量程式碼/相依性的組件不太可能在初始渲染您的應用程式時使用,您可以使用 React 的 lazy
API 來延遲載入其程式碼,直到首次渲染時才加載。通常,您應該考慮延遲載入應用程式中的螢幕級組件,以便向您的應用程式添加新螢幕不會增加其啟動時間。
在 React 的文件中,閱讀更多關於使用 Suspense 延遲載入組件的資訊,包括程式碼範例。
提示:避免模組副作用
如果您的組件模組(或其相依性)具有副作用,例如修改全域變數或訂閱組件外部的事件,則延遲載入組件可能會改變您應用程式的行為。React 應用程式中的大多數模組都不應有任何副作用。
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()
函數來做到這一點。
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>;
}
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
) 內部。
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
module.exports = {
transformer: {
async getTransformOptions() {
return {
transform: {
inlineRequires: false,
},
};
},
},
};
若要僅從 require
內聯中排除某些模組:
有兩個相關的轉換器選項:inlineRequires.blockList
和 nonInlinedRequires
。請參閱程式碼片段,以取得如何使用每個選項的範例。
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
- iOS
在 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"]
]
在 iOS 上,RAM 捆綁包始終是索引的(= 單一檔案)。
在 Xcode 中,透過編輯建置階段 "Bundle React Native code and images" 來啟用 RAM 格式。在 ../node_modules/react-native/scripts/react-native-xcode.sh
之前,添加 export BUNDLE_COMMAND="ram-bundle"
export BUNDLE_COMMAND="ram-bundle"
export NODE_BINARY=node
../node_modules/react-native/scripts/react-native-xcode.sh
請參閱 Metro 中 getTransformOptions
的文件,以取得更多關於設定和微調您的 RAM 捆綁包建置的詳細資訊。