Будь то компонент класса или функциональный компонент,React
Основные направления оптимизации производительности следующие:
- уменьшить ре
render
количество раз - сократить вычисления
- Уменьшите количество узлов для рендеринга, уменьшите объем рендеринга
- Разумный дизайн компонентов
1. Уменьшите количество повторных рендеров
React
Самая тяжелая (самая трудоемкая) частьreconciliation
, переводится как примирение, примирение.reconciliation
Конечной целью является обновление с новым состоянием наиболее эффективным способом.UI
, мы можем просто понимать какdiff
. если этого не произойдетrender
, это не произойдетreconciliation
.
1. Используйте React.memo для кэширования компонентов
React.memo
в16.6
добавлено вAPI
,а такжеPureComponent
похоже на сокращение повторногоrender
количество раз.
Начнем с вопроса:
index.js
:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const App = () => {
const [title, setTitle] = useState("这是一个 title");
return (
<div className="App">
<h1>{title}</h1>
<button onClick={() => setTitle("title 已经改变")}>改名字</button>
<Child name="陈星星" />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
который содержит подкомпонентChild
.
Child.js
:
import React from "react";
const Child = ({ name }) => {
console.log(name);
return <h1>{name}</h1>;
};
export default Child;
При первом рендеринге на консоли будет напечатано «Chen Xingxing». при нажатии на страницуbutton
изменитьtitle
час,title
изменено, но "Chen Xingxing" также будет распечатано,и перешел кChild
компонентprops
не изменился. Мы знаем, что причина в том, что…Когда родительский компонент сбрасываетсяrender
также вызовет перезагрузку подкомпонентовrender
.
ПредположениеChild
Компонент — это очень большой компонент, в котором выполняется множество вычислений, напримерChild
Когда рендеринг компонентов потребляет много производительности, мы должны уменьшить или избежать его.Child
Ненужный рендеринг компонентов. Итак, наша точка оптимизации -передается дочерним компонентамprops
Когда нет изменений, пусть дочерний компонент не отображается.
В функциональных компонентах мы можем использоватьReact.memo
сделать это,memo
которыйmemorized
, значит помнить.
React.memo
является компонентом более высокого порядка, который можно использовать для обертывания функционального компонента вprops
В случае неизменности обернутый компонент не будет перерисовываться.React.memo
параметры будутповерхностное сравнение, пропустить, если ссылка та жеrender
этап, непосредственно использующий результат предыдущей памяти, т.е.компонент кэша.
Так что мы можемChild
Модификация компонента:
// ...
export default React.memo(Child);
из-заReact.memo
По умолчанию будет выполняться только поверхностное сравнение параметров, если мы хотим контролировать процесс сравнения,React.memo
Он поддерживает второй параметр, и мы можем реализовать пользовательскую функцию сравнения, передав второй параметр.
const MyComponent = (props) => {
/* 使用 props 渲染 */
};
const areEqual = (prevProps, nextProps) => {
/*
如果把 nextProps 传入 render 方法的返回结果与
将 prevProps 传入 render 方法的返回结果一致则返回 true,
否则返回 false
*/
};
export default React.memo(MyComponent, areEqual);
При сравнении мы должны сделать как можно большеУточнение сравнения.
Но здесь нам нужноУведомление:а такжеclass
компонентshouldComponentUpdate()
Отличие в том, что с помощьюReact.memo
когда, еслиprops
равный,areEqual
вернусьtrue
;еслиprops
не равно, возвратfalse
. Это то же самое, чтоshouldComponentUpdate
Возвращаемое значение метода противоположно.
2. Используйте React.useCallback для кэширования ссылок
Теперь давайте немного изменим приведенный выше пример, чтобы дать дочернему компонентуChild
передать функциюprops
.
App.js
:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const App = () => {
const [title, setTitle] = useState("这是一个 title");
const print = () => {
console.log("I am a function props");
};
return (
<div className="App">
<h1>{title}</h1>
<button onClick={() => setTitle("标题改变了")}>改标题</button>
<Child name="陈星星" onClick={print} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Child.js
:
import React from "react";
const Child = ({ name, onClick }) => {
console.log(name);
return <h1>{name}</h1>;
};
export default React.memo(Child);
мы использовалиReact.memo
, и перешли кChild
компонентprops
Кажется, не изменился, но щелкнитеbutton
После этого «Чэнь Син» все еще печатается, почему?
потому что каждый щелчокbutton
При обновлении родительского компонентаДля функциональных компонентов каждый разrender
перезапустит вызов функции с нуля, поэтому передается дочернему компонентуChild
Функцияprint
Каждый раз он генерируется заново. Функции — это данные ссылочного типа, и генерируемые каждый раз адреса, естественно, будут разными. следовательно,React
рассмотрит подкомпонентыprops
Изменено, дочерний компонент будет повторно отображаться.
Аналогично, то же самое происходит, если дочернему компоненту передается обычная ссылочная переменная:
App.js
:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const App = () => {
const [title, setTitle] = useState("这是一个 title");
const a = [1];
return (
<div className="App">
<h1>{title}</h1>
<button onClick={() => setTitle("标题改变了")}>改标题</button>
<Child name="陈星星" a={a} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Child.js
:
import React from "react";
const Child = ({ name, a }) => {
console.log(name);
return <h1>{name}</h1>;
};
export default React.memo(Child);
Как это решить?
(1) Поместите функцию или ссылочную переменную вне компонента
Мы передаем функциюprops
Разве не было бы достаточно поместить его вне компонента? Да, это позволяет избежать проблемы повторного рендеринга дочерних компонентов:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const print = () => {
console.log("I am a function props");
};
const a = [1];
const App = () => {
const [title, setTitle] = useState("这是一个 title");
return (
<div className="App">
<h1>{title}</h1>
<button onClick={() => setTitle("标题改变了")}>改标题</button>
<Child name="陈星星" onClick={print} a={a} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Для некоторых, которые не относятся к компоненту (не связаны с компонентомprops
илиstate
вычисляемая) переменная, мы должны поместить ее вне компонента.
Но если переданная функция должна зависеть отstate
Что мы можем сделать по этому поводу? Это нужно использоватьReact
который предоставилuseCallback
этоAPI
.
(2) использоватьuseCallback
useCallback
вернутьmemoized
Функция обратного вызова, которая принимает два параметра, первый параметр — это функция, а второй параметр — массив зависимостей. Он вернет новую функцию только в том случае, если значение в зависимом массиве изменилось, в противном случае будет использоваться предыдущее значение.функция памяти.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Итак, мы можем трансформироватьprint
функция:
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const App = () => {
const [title, setTitle] = useState("这是一个 title");
const [subtitle, setSubtitle] = useState("这是一个 subtitle");
const print = useCallback(() => {
console.log("I am a function props");
}, [subtitle]);
return (
<div className="App">
<h1>{title}</h1>
<h1>{subtitle}</h1>
<button onClick={() => setTitle("标题改变了")}>改标题</button>
<button onClick={() => setSubtitle("副标题改变了")}>改副标题</button>
<Child name="陈星星" onClick={print} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
В приведенном выше примере только тогда, когдаsubtitle
Когда это состояние изменится, оно будет восстановленоprint
функция, дочерний компонент будет обновлен.
использоватьuseCallback
При передаче пустого массива в качестве массива зависимостей родительский компонент больше не влияет на дочерний компонент, а параметры в функции, принимаемой дочерним компонентом, всегда инициализируются для использования.useCallback
значение, например:
App.js
:
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import Child from "./Child";
const App = () => {
const [title, setTitle] = useState("这是一个 title");
const [subtitle, setSubtitle] = useState("这是一个 subtitle");
const print = useCallback(() => {
console.log(`title: ${title}`);
}, []);
return (
<div className="App">
<h1>{title}</h1>
<h1>{subtitle}</h1>
<button onClick={() => setTitle("标题改变了")}>改标题</button>
<button onClick={() => setSubtitle("副标题改变了")}>改副标题</button>
<Child name="陈星星" onClick={print} />
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
Child.js
:
import React from "react";
const Child = ({ onClick }) => <button onClick={onClick}>print</button>;
export default React.memo(Child);
Получить только первую инициализациюtitle
значение, такой результат не то, что нам нужно. Если такой результат желателен, то он также должен бытьprint
Функции размещаются вне компонента.
3. Избегайте использования анонимных функций
Давайте посмотрим на кусок кода:
const Main = ({ list }) => (
<List>
{list.map((i) => (
<Item key={i.id} onClick={() => handleDelete(i.id)} value={i.value} />
))}
</List>
);
Этот код очень распространен, хотя есть возможность передавать функции с помощью анонимных функций, и он выглядит более лаконичным. Но это проблематично, анонимная функция имеет другую ссылку каждый раз, когда она отображается, что по-прежнему вызываетItem
Проблема повторного рендеринга компонентов.
Мы также можем использоватьReact.useCallback
оптимизировать:
const Main = ({ list }) => {
const handleDelete = useCallback(
(id) => () => {
// ...
},
[],
);
return (
<List>
{list.map((i) => (
<Item key={i.id} id={i.id} onClick={handleDelete(id)} value={i.value} />
))}
</List>
);
};
4. Избегайте повторного рендеринга, вызванного React Context
React 16
предоставлено китайско-сингапурскимContext API
Есть особенность, которая когда-тоContext
изValue
изменения, все зависит отContext
Все компоненты будут вынуждены повторно рендериться, это проницаемоReact.memo
илиshouldComponentUpdate
Сравнение перехвачено. См. пример:
Context.js
:
import { createContext } from "react";
const context = createContext();
export default context;
App.js
:
import React, { useContext, useState } from "react";
import Context from "./Context";
const redTheme = {
color: "red",
};
const greenTheme = {
color: "green",
};
const Content = () => {
const { theme, switchTheme } = useContext(Context);
return (
<>
<h1 style={theme}>Hello world</h1>
<button onClick={() => switchTheme(redTheme)}>Red Theme</button>
<button onClick={() => switchTheme(greenTheme)}>Green Theme</button>
</>
);
};
const Header = () => {
console.log("render Header");
return <h1>Hello CodeSandbox</h1>;
};
const App = () => {
const [theme, setTheme] = useState(redTheme);
console.log("render App");
return (
<Context.Provider value={{ theme, switchTheme: setTheme }}>
<div>
<Header />
<Content />
</div>
</Context.Provider>
);
};
export default App;
Эффект от запуска в браузере следующий:
Как избежать этой проблемы? Идея проста,нужен способ сказатьContext.Provider
, сообщая ему, что дочерний компонент не изменился.
Согласно этой идее реализация такова: построить независимый компонент для управленияstate
а такжеProvider
, напишите подкомпоненты вне этого компонента.
Улучшенный код:
import React, { useContext, useState } from "react";
import Context from "./Context";
const redTheme = {
color: "red",
};
const greenTheme = {
color: "green",
};
const Content = () => {
const { theme, switchTheme } = useContext(Context);
return (
<>
<h1 style={theme}>Hello world</h1>
<button onClick={() => switchTheme(redTheme)}>Red Theme</button>
<button onClick={() => switchTheme(greenTheme)}>Green Theme</button>
</>
);
};
const Header = () => {
console.log("render Header");
return <h1>Hello CodeSandbox</h1>;
};
const ThemeProvider = (props) => {
const [theme, setTheme] = useState(redTheme);
return (
<Context.Provider value={{ theme, switchTheme: setTheme }}>
{props.children}
</Context.Provider>
);
};
const App = () => {
console.log("render App");
return (
<ThemeProvider>
<Header />
<Content />
</ThemeProvider>
);
};
export default App;
Это позволяет избежать проблемы повторного рендеринга.
Итак, мы используемContext API
, требуется особая осторожность.
5. Избегайте использования встроенных объектов
взять верхReact Context
Для примера кодаThemeProvider
На самом деле, есть еще проблема:
const ThemeProvider = (props) => {
const [theme, setTheme] = useState(redTheme);
return (
/*
value 值使用内联对象时,react 会在每次渲染时重新创建对此对象的引用,
这会导致接收此对象的组件将其视为不同的对象,
因此,该组件对于 prop 的浅层比较始终返回 false
这会导致所有依赖于该 Context 的组件被强制重新渲染。
*/
<Context.Provider value={{ theme, switchTheme: setTheme }}>
{props.children}
</Context.Provider>
);
};
мы можем использоватьuseMemo
Кэшировать это:
const ThemeProvider = (props) => {
const [theme, setTheme] = useState(redTheme);
const value = useMemo(() => ({ theme, setTheme }), [theme]); // 缓存
return <Context.Provider value={value}>{props.children}</Context.Provider>;
};
2. Уменьшить количество вычислений
Оптимизации, уменьшающие объем вычислений, можно разделить на две категории:
- Один - уменьшить
React
Вычисление самого фреймворка, например построение виртуальногоDOM
расчет,diff
расчет; - Другой заключается в сокращении логических вычислений бизнес-кода.
1. Уменьшите ненужную вложенность узлов
мы знаемReact
используя виртуальныйDOM
,React
использоватьJavaScript
представление объектаDOM
узел.DOM
Узлы включают метки, атрибуты и дочерние узлы.
React
Генерирует и поддерживает в реальном времени в памятиDOM
такой же виртуальныйDOM
дерево, после изменения компонента оно сгенерирует новыйDOM
,React
поставит новый виртуальныйDOM
следуйте оригинальному виртуальномуDOM
Сравните и найдите дваDOM
различные места(diff
), затем поставитьdiff
Поместите его в очередь и, наконец, обновите его партиями.diff
к реальномуDOM
начальство.
так,Уменьшите ненужную вложенность узлов, очень нужно. Ненужные элементы следует удалить, например:
<div className="App">
<div>
<h1>count:{num}</h1>
</div>
<div>
<p>This is a line.</p>
</div>
</div>
h1
этикетки иp
Ярлыки совершенно не нужныdiv
завернутые в этикетки.
Я любил использоватьstyled-componentsЭта библиотека, которая позже была признана совершенно ненужной, только улучшает читабельность кода (я так думаю). Его интенсивное использование может сделать узлы очень глубоко вложенными, а его схема стилей имеет проблемы с производительностью (о которых я расскажу позже).
а также,Много ненужной вложенности узлов вызвано злоупотреблением компонентами более высокого порядка., для компонентов более высокого порядка мы должны придерживаться принципа: использовать его только при необходимости. использовать как можно большеprops
,Hooks
вместо.
В то же время мы также можемиспользоватьFragment
чтобы не добавлять доп.DOM
,Такие как:
const Main = () => (
<>
<h1>Hello world!</h1>
<h1>Hello react!</h1>
</>
);
2. Используйте ключи
keys
может помочьReact
Отслеживайте, какие элементы были изменены, добавлены или удалены из списка.React
исходный кодkey
сравнение, если оно отличается, оно будет обновлено напрямую.
элементkey
Желательно уникальную строку, которая есть у этого элемента в списке. Обычно мы используем данные изid
как элементkey
.
если не добавитьkey
значение илиkey
В чем может быть проблема с тем же значением?
-
react
Предупреждение будет дано - падение производительности
-
key
Когда значения одинаковы, это может привести к тому, что данные будут хаотичными при обновлении данных.
3. Выберите лучшую схему обработки стиля
React
Существует три распространенных способа установкиUI
стиль:
Inline Style
CSS Classes
CSS Modules
Их сравнение производительности примерно:CSS Module
> CSS Classes
> Inline Style
.
Много раз мы использовали встроенные стили для удобства, что очень требовательно к производительности. При использовании встроенных стилей, потому что стиль верблюжьего регистра, который мы на самом деле написали, по-прежнему принадлежитJSX
, на самом деле не является стилем, браузеру требуется больше времени для обработки сценариев и рендеринга, добавления процесса отображения и передачи правил стиля.
Так, в проектепредварительный выборCSS Module
Сюда.
Подробное сравнение см.
4. Используйте useMemo для кэширования результатов вычислений
и уменьшитьrender
Точно так же, как и количество раз, мы можем кэшировать некоторые результаты расчета, если параметры не меняются, мы можем напрямую использовать предыдущие результаты расчета.React Hook
предоставлено вuseMemo
изAPI
реализовать.
// computeExpensiveValue(a, b) 是一个计算量很大的函数
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
-
useMemo
Первый параметр — это функция, значение, возвращаемое этой функцией, будет кэшироваться, и это значение будет использоваться какuseMemo
возвращаемое значение; - Второй параметр является зависимостью от массива.Если значение в массиве изменится, функция в первом параметре будет выполнена повторно, а значение, возвращаемое функцией, будет кэшировано и использовано как
useMemo
Возвращаемое значение.
См. пример:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [num, setNum] = useState(0);
// 一个非常耗时的一个计算函数
// result 最后返回的值是 499999500000
const expensiveFn = () => {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += i;
}
console.log(result); // 499999500000
return result;
};
const base = expensiveFn();
return (
<div className="App">
<h1>count:{num}</h1>
<button onClick={() => setNum(num + base)}>+1</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
За исключением начального рендера, каждый щелчокbutton
, он будет выполнен повторноexpensiveFn
Расчеты в , используют следующееuseMemo
оптимизировать:
const base = useMemo(expensiveFn, []);
Выполняется только при начальном рендерингеexpensiveFn
и кешировать результат, повторить кликbutton
, больше не будет печататьexpensiveFn
результат функции, котораяexpensiveFn
Функция больше не выполняется, и достигается эффект оптимизации.
5. Кэшировать дочерние узлы
теперь, когдаuseMemo
Результат выполнения функции можно кэшировать, если функция возвращает дочерний узел, можно ли его тоже использовать?useMemo
кешировать это? Ответ положительный.
const Parent = ({ a, b }) => {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
);
};
Примечание: таким образомнедействителен в цикле,потому чтоHook
передачане можемпомещается в петлю. Но мы можем выделить отдельный компонент для элементов списка и вызвать егоuseMemo
.
Меры предосторожности
использоватьuseMemo
, есть несколько замечаний:
- если не прошел
useMemo
Второй параметр , по-прежнему будет вычислять новое значение каждый раз при рендеринге; -
перейти к
useMemo
функция должна быть запущена во время рендеринга,не хочуuseMemo
делать все, что не будет сделано во время рендеринга, побочные эффектыuseEffect
, вместоuseMemo
; - Если это функция расчета с небольшим количеством вычислений, вы также можете не использовать
useMemo
, потому что эта оптимизация не будет узким местом в производительности, но может вызвать некоторые проблемы с производительностью при неправильном использовании.
6. Дросселирование и защита от тряски
О концепции троттлинга и антишейка можно узнать отсюда:вести блог Talent/JavaScript-…
существуетReact
Существует очень распространенный сценарий: ввод содержимого в поле ввода в бэкэнд (при условии, чтоapi/users
) для запроса данных и отображения списка пользователей под полем ввода.
Здесь будет проблема: каждый раз, когда мы вводим символ в поле ввода, он будет запускать асинхронный сетевой запрос, запрашиваяapi/users
Получите список пользователей для отображения и обновите его в случае успехаstate
обновитьDOM
, который очень требователен к производительности.
Иногда мы будем поддерживать «список пользователей» в памяти, чтобы уменьшить нагрузку на внутренний сервер, чтобы избежать частых сетевых запросов, но все равно необходимо выполнять дорогостоящую обработку входных символов.DOM
возобновить.
Здесь мы можем использовать функцию троттлинга и анти-тряски для оптимизации производительности, рекомендуется использоватьloadsh
функции в библиотеке.
3. Уменьшите количество узлов рендеринга и уменьшите объем рендеринга
1. Оптимизируйте условный рендеринг
Мы часто используем условный рендеринг, см. следующий пример:
const App = () => {
const [name, setName] = useState("react");
if (name === "react") {
return (
<>
<HeaderComponent>header</HeaderComponent>
<ContentComponent>content</ContentComponent>
<FooterComponent>footer</FooterComponent>
</>
);
}
return (
<>
<ContentComponent>content</ContentComponent>
<FooterComponent>footer</FooterComponent>
</>
);
};
когдаname
для'react'
будет отображаться, когдаHeaderComponent
компоненты. братьJavaScript
С точки зрения синтаксиса проблем с производительностью нет. Но мы пишемReact
, у него проблемы с производительностью.
Каждый раз, когда состояние изменяется и перерисовывается,React
сначала проверюname
Является ли значение'react'
, затем вdiff
которые определяются в алгоритмеDOM
Узел изменился.
И написанное выше, в каждом исполненииdiff
когда, еслиname
изменился,React
подумал быHeaderComponent
Компонент недоступен, сейчас изменились первый и второй компоненты, которые нужно отрисовать, поэтомуReact
удалит и переустановит компоненты в позиции 1 и 2, что совершенно не нужно, т.к.ContentComponent
компоненты иFooterComponent
Компоненты не изменились.
Мы можем оптимизировать так:
<>
{name === "react" && <HeaderComponent>header</HeaderComponent>}
<ContentComponent>content</ContentComponent>
<FooterComponent>footer</FooterComponent>
</>
когдаname
нет'react'
час,React
поставить на 1 местоnull
, компоненты в позиции 2 и позиции 3 остаются без изменений. Поскольку элемент не изменился, компонент не будет выгружен, что сокращает количество ненужных операций.
2. Виртуальный список
Виртуальный список — распространенный метод оптимизации для «длинного списка» и «сложного дерева компонентов», суть его оптимизации заключается в уменьшении количества отображаемых узлов.Виртуальный список отображает только видимые элементы видимого в данный момент окна.. Как показано ниже:
(картинка взята из интернета)
Сценарии использования виртуальных списков:
- Бесконечный прокручиваемый список
- Неограниченное переключение календаря или карусели
- Лента новостей
- ...
Есть две библиотеки, которые используются больше:
- react-virtualized
-
react-window(легче
react-virtualized
, автор тот же человек)
Ниже сreact-windowПример:
import { FixedSizeList as List } from "react-window";
const Row = ({ index, style }) => <div style={style}>Row {index}</div>;
const Example = () => (
<List height={150} itemCount={1000} itemSize={35} width={300}>
{Row}
</List>
);
Эффект:
о большемreact-windowКак использовать можно перейти на официальный сайт, чтобы проверить.
3. Ленивая загрузка
ленивая загрузка(Lazy loading
) также известен какОтложить загрузку, что по сути так же, как виртуальный список -Отрисовывайте соответствующие узлы только при необходимости и не отображайте соответствующие ресурсы, если они не нужны для просмотра или использования в определенное время..
НапримерAnt Design
изDrawer
Компонент ящика, при первом отображении ящик не отображается, соответствующийvisible
этоprops
По умолчаниюfalse
, затем вrender
Лиreturn null
, компонент ящика не отображается,DOM
В дереве нет соответствующей структуры, при нажатии на кнопкуvisible
свойство установлено наtrue
, визуализируйте компонент ящика, а затем нажмите кнопкуvisible
дляfalse
, компонент в это время скрыт, но вDOM
еще в дереве.
Преимуществ у этого много:
- Во время начальной загрузки это может значительно снизить нагрузку на клиентский рендеринг и повысить скорость загрузки страницы;
- Уменьшить загрузку недействительных ресурсов, что позволяет значительно снизить нагрузку и трафик на сервер, а также снизить нагрузку на браузер;
- Предотвращение блокировки слишком большого количества одновременно загружаемых ресурсов
js
загрузка, влияющая на нормальное использование веб-сайта.
Существует множество сценариев, в которых используется отложенная загрузка, например:
- выпадающий список
- модальное поле
- селектор дерева
- Складные компоненты
Библиотеки, которые часто используются для реализации ленивой загрузки:
-
react-lazyload, вот
demo
:GitHub.com/глубокий космос/… - react-loadable
-
React 16.6
Введены новые функции:React.lazy
а такжеsuspense
;требует внимания,React.lazy
неподходящийSSR
.
Об использованииReact.lazy
а такжеsuspense
Для примера реализации ленивой загрузки вы можете обратиться к:GitHub.com/глубокий космос/…
4. Предварительная загрузка
Ранее упоминалась ленивая загрузка, которая может повысить скорость начальной загрузки страницы, но для сложных компонентов больших проектов затраты времени на загрузку компонента очень велики, что может привести кloading
Дисплей очень длинный, что влияет на удобство использования.
Мы можем предварительно загрузить (preloading
) является компонентом, но он не отображается на странице, то есть скрытый рендеринг, когда поведение пользователя запускает отображение, просто отображать его напрямую, чтобы операция пользователя могла быть отражена как можно быстрее.
Наиболее популярные библиотеки:
Эти две библиотеки также доступны черезReact
Официально рекомендуется.
также можно использоватьReact 16.6
Введены новые функции:React.lazy
а такжеsuspense
.
Об использованииReact.lazy
а такжеsuspense
Для примера реализации предварительной загрузки вы можете обратиться к:GitHub.com/глубокий космос/…
4. Разумный дизайн компонентов
1. Единая ответственность
Да, правильное проектирование компонентов также повысит производительность.Дизайн компонентов должен строго следовать принципу единой ответственности.. См. пример:
(картинка взята из интернета)
MyComponent
компонент зависит отA
,B
,C
Три компонента, если компоненты разработаны в соответствии с этой структурой, до тех пор, покаA
,B
,C
любое изменение, тоMyComponent
Целое будет перерисовано.
Основываясь на принципе единой ответственности, мы можем разработать следующее:
(картинка взята из интернета)
БудуA
,B
,C
извлекаются в соответствующие компоненты, и теперьA
Меняется только рендерA
сам компонент, не затрагивая родительский компонент иB
,C
компоненты.
Типичным примером является рендеринг списка, где элементы списка должны быть абстрагированы как отдельные компоненты. Таким образом, изменение элемента списка не повлияет на весь большой компонент.
2. Разумное состояние
Не все данные или состояние нужно размещать в компоненте.state
, принцип таков: данные, которые должны реагировать на его изменения или должны быть отображены в представлении, должны быть помещены вstate
середина. Это позволяет избежать ненужных изменений данных, вызывающих повторную визуализацию компонента.
3. Разумный реквизит
Если компонентprops
Если он будет слишком сложным, его будет очень сложно поддерживать, что повлияет на тестирование и отладку, что также нарушает принцип единой ответственности компонентов; это также повлияет наshallowCompare
эффективность, снижениеЧастота попаданий в кэш компонентов. Таким образом, дляprops
Сложные компоненты следует разбирать более детально и разумно проектировать.
5. Расширение
Есть и другие способы оптимизацииReact
заявление.
1. Рендеринг на стороне сервера
React
Предусмотрено два методаrenderToString
а такжеrenderToStaticMarkup
используется для соединения компонентов (Virtual DOM
) вывод какHTML
строка, котораяReact
Рендеринг на стороне сервера (Server-Side Rendering
, именуемыйSSR
)Основы. Наиболее популярные библиотеки:
2. Используйте производственную версию
Используйте режим разработки при разработке приложения и рабочий режим при развертывании приложения для пользователей.
Если предмет переданCreate React Appпостроено, запущено:
$ npm run build
Эта команда будет в проекте подbuild/
Соответствующая производственная версия создается в каталоге. Эту команду необходимо выполнять только перед производственным развертыванием, обычной разработкой и использованием.npm start
Вот и все.
6. Наконец
Эта статья ссылается на множество отличных статей в Интернете, и в некоторых местах могут быть сходства.