Przejdź do treści głównej
Wersja: 0.79

Optymalizacja ładowania JavaScript

Nieoficjalne Tłumaczenie Beta

Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →

Parsowanie i uruchamianie kodu JavaScript wymaga pamięci i czasu. Dlatego wraz ze wzrostem aplikacji warto opóźniać ładowanie kodu do momentu pierwszego zapotrzebowania. React Native zawiera domyślnie włączone optymalizacje, a dodatkowo możesz zastosować techniki w swoim kodzie, które pomogą React efektywniej ładować aplikację. Istnieją też zaawansowane automatyczne optymalizacje (z własnymi kompromisami) odpowiednie dla bardzo dużych aplikacji.

Zalecane: Używaj Hermesa

Hermes to domyślny silnik dla nowych aplikacji React Native, wyspecjalizowany w efektywnym ładowaniu kodu. W wersjach produkcyjnych kod JavaScript jest w pełni kompilowany do bytecode'u z wyprzedzeniem. Bytecode jest ładowany do pamięci na żądanie i nie wymaga parsowania jak zwykły JavaScript.

informacja

Więcej o używaniu Hermesa w React Native przeczytasz tutaj.

Zalecane: Leniwie ładuj duże komponenty

Jeśli komponent z dużą ilością kodu/zależności prawdopodobnie nie będzie używany podczas początkowego renderowania aplikacji, możesz użyć Reactowego API lazy by opóźnić ładowanie jego kodu do pierwszego renderowania. Zazwyczaj warto rozważyć leniwe ładowanie komponentów na poziomie ekranów, aby dodawanie nowych ekranów nie zwiększało czasu uruchamiania aplikacji.

informacja

Więcej o leniwym ładowaniu komponentów z Suspense, w tym przykłady kodu, znajdziesz w dokumentacji Reacta.

Wskazówka: Unikaj efektów ubocznych modułów

Leniwe ładowanie komponentów może zmienić zachowanie aplikacji, jeśli moduły komponentów (lub ich zależności) mają efekty uboczne, jak modyfikowanie zmiennych globalnych czy subskrybowanie zdarzeń poza komponentem. Większość modułów w aplikacjach React nie powinna mieć efektów ubocznych.

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() {
// ...
}

Zaawansowane: Wywołuj require w miejscu

Czasami możesz chcieć opóźnić ładowanie kodu do pierwszego użycia bez korzystania z lazy lub asynchronicznego import(). Możesz to osiągnąć używając funkcji require() w miejscu, gdzie normalnie użyłbyś statycznego import na początku pliku.

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>
);
}

Zaawansowane: Automatyczne wstawianie wywołań require

Jeśli używasz React Native CLI do budowania aplikacji, wywołania require (ale nie import) będą automatycznie wstawiane zarówno w twoim kodzie, jak i w zewnętrznych pakietach (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>
);
}
informacja

Niektóre frameworki React Native wyłączają to zachowanie. W szczególności w projektach Expo wywołania require domyślnie nie są wstawiane. Możesz włączyć tę optymalizację edytując konfigurację Metro projektu i ustawiając inlineRequires: true w getTransformOptions.

Pułapki wstawiania require

Wstawianie wywołań require zmienia kolejność ewaluacji modułów i może nawet spowodować, że niektóre moduły nigdy nie zostaną ewaluowane. Zwykle jest to bezpieczne dzięki temu, że moduły JavaScript często pisane są bez efektów ubocznych.

Jeśli któryś z twoich modułów ma efekty uboczne - np. inicjalizuje mechanizm logowania czy modyfikuje globalne API używane przez resztę kodu - możesz doświadczyć nieoczekiwanego zachowania lub nawet błędów. W takich przypadkach warto wykluczyć konkretne moduły z optymalizacji lub całkowicie ją wyłączyć.

Aby wyłączyć wszystkie automatyczne wstawianie wywołań require:

Zaktualizuj metro.config.js ustawiając opcję transformera inlineRequires na false:

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

Aby wykluczyć tylko niektóre moduły z wstawiania require:

Istnieją dwie istotne opcje transformera: inlineRequires.blockList i nonInlinedRequires. W przykładzie kodu zobaczysz jak użyć każdej z nich.

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',
],
},
};
},
},
};

Więcej szczegółów na temat konfiguracji i precyzyjnego dostosowywania wbudowanych wywołań require znajdziesz w dokumentacji getTransformOptions w Metro.

Zaawansowane: Korzystanie z pakietów modułów z dostępem swobodnym (bez Hermesa)

informacja

Nieobsługiwane przy używaniu Hermesa. Bytecode Hermesa nie jest kompatybilny z formatem pakietów RAM i zapewnia taką samą (lub lepszą) wydajność we wszystkich przypadkach.

Pakiety modułów z dostępem swobodnym (znane też jako pakiety RAM) współdziałają z technikami opisanymi powyżej, aby ograniczyć ilość kodu JavaScript wymagającą parsowania i wczytania do pamięci. Każdy moduł przechowywany jest jako osobny ciąg znaków (lub plik), który jest parsowany dopiero w momencie konieczności wykonania modułu.

Pakiety RAM mogą być fizycznie podzielone na osobne pliki lub wykorzystywać format indeksowany, składający się z tabeli przeglądowej wielu modułów w jednym pliku.

On Android enable the RAM format by editing your android/app/build.gradle file. Before the line apply from: "../../node_modules/react-native/react.gradle" add or amend the project.ext.react block:

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

Use the following lines on Android if you want to use a single indexed file:

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

Szczegółowe informacje o konfiguracji i dostosowywaniu budowania pakietów RAM znajdziesz w dokumentacji getTransformOptions w Metro.