Оптимизация производительности функциональных компонентов React

React.js

Будь то компонент класса или функциональный компонент,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 api 重复渲染

Как избежать этой проблемы? Идея проста,нужен способ сказать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В чем может быть проблема с тем же значением?

  1. reactПредупреждение будет дано
  2. падение производительности
  3. 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-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.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. Наконец

Эта статья ссылается на множество отличных статей в Интернете, и в некоторых местах могут быть сходства.