Оптимизация React с неизменяемым

внешний интерфейс React.js

Статья синхронизирована с GithubPines-Cheng/blog

У React есть уникальный способ сокращения повторного рендеринга, то есть виртуальный DOM, но очевидно, что React не может превзойти нативную скорость при первом рендеринге, или он должен иметь возможность сравнивать с другими фреймворками. Особенно в React до оптимизации рендеринг будет выполняться каждый раз при изменении данных, что сильно влияет на производительность, особенно на мобильной стороне.

Поведение рендеринга React по умолчанию

Инициализировать рендеринг

При инициализации рендеринга нам нужно рендерить все приложение
(зеленый = визуализированный узел)
image

предлагать изменения

Мы хотим обновить часть данных. Эти изменения связаны только с одним конечным узлом (зеленым).

image

идеальное обновление

Мы хотим отображать только эти несколько узлов (зеленые) на критическом пути к листовым узлам.
image

Поведение по умолчанию

Если вы не скажете React не делать этого, он будет (оранжевый = потраченный впустую рендеринг)

image

Как видно из приведенного выше рисунка, в дополнение к трем узлам, которые необходимо отрисовывать, компонент также отрисовывает другие узлы, которые не нужно отображать, что является большой потерей производительности. Для сложных страниц это приведет к очень плохому общему восприятию страницы. Поэтому для повышения производительности компонентов следует сделать все возможное, чтобы уменьшить ненужный рендеринг.

Жизненный цикл реакции

Жизненный цикл React выглядит следующим образом, с ним могут ознакомиться студенты, которые с ним не знакомы.
image

shouldComponentUpdate

из-заshouldComponentUpdateЭто ключ к оптимизации. Реакция по оптимизации рендеринга основного рендеринга повторяется фактически выполнение данных о сравнении данных, необходимого внутри. Перед оптимизацией,shouldComponentUpdateВозвращает true по умолчанию, что приводит к повторному рендерингу компонента всякий раз, когда инициируются какие-либо изменения данных. Это неизбежно приведет к напрасной трате ресурсов и низкой производительности — вы можете чувствовать себя медленнее, чем нативный ответ.

Ключом к оптимизации производительности React являетсяshouldComponentUpdate,
image

В приведенном выше примере, поскольку C2shouldComponentUpdateВозвращая false, React не нужно генерировать новый виртуальный DOM, поэтому ему не нужно обновлять DOM.Обратите внимание, что React даже не нужно вызывать C4 и C5.shouldComponentUpdate.

C1 и C3shouldComponentUpdateВозвращает true, поэтому React должен перейти к листовым узлам, чтобы проверить их, C6 возвращает true, потому что виртуальные DOM не равны, и DOM необходимо обновить. Последний интерес представляет C8, для этого узла React нужно вычислить виртуальный DOM, но так как он равен старому, обновлять DOM не нужно.

React.PureComponent

Когда в компонент передается только один слой реквизита и состояния, мы можем использовать его напрямую. React.PureComponent, это автоматически поможет нам с неглубоким сравнением, тем самым контролируя возвращаемое значение shouldComponentUpdate.

Однако, когда передается более одного уровня реквизита или состояния или когда не передается ни массив, ни объект, неглубокие сравнения (shallow-compare) не удастся. Конечно, мы также можемshouldComponentUpdate()использовать в использованииdeepCopyа такжеdeepCompareВо избежание ненужныхrender(),ноdeepCopyа такжеdeepCompare Как правило, это очень требовательно к производительности. В это время нам нужноImmutable.

Immutable

Объекты в JavaScript, как правило, изменяемы.Из-за использования присваивания ссылки новый объект просто ссылается на исходный объект, и изменение нового объекта повлияет на исходный объект. Такие как

foo={a: 1}; 
bar=foo; 
bar.a=2

Вы обнаружите, что в это время значение foo.a также изменилось на 2. Хотя это может сэкономить память, когда приложение сложное, это создает очень большую скрытую опасность, и преимущества, приносимые Mutable, становятся больше, чем выигрыш. Для решения этой проблемы обычно используютshallowCopy(Неглубокая копия) илиdeepCopy(Глубокая копия), чтобы избежать изменения, но это приводит к трате ресурсов ЦП и памяти.

И Immutable может очень хорошо решить эти проблемы.

Что такое неизменяемые данные

Immutable DataЭто данные, которые после создания не могут быть изменены. Любая модификация, добавление или удаление объекта Immutable возвращает новый объект Immutable. Принцип реализации ImmutablePersistent Data Structure(Постоянная структура данных), то есть при использовании старых данных для создания новых данных необходимо обеспечить доступность и неизменность старых данных одновременно. В то же время, чтобы избежать потери производительности, вызванной копированием всех узлов deepCopy, Immutable используетStructural Sharing(Совместное использование структуры), то есть, если узел в дереве объектов изменяется, модифицируются только этот узел и затрагиваемый им родительский узел, а остальные узлы являются общими.

Посмотрите эту классическую анимацию ниже:

image

immutable.js

Immutable.jsПо сути, это библиотека персистентных структур данных JavaScript, но из-за популярности React в тот же период и беспрепятственного взаимодействия с React с точки зрения оптимизации производительности люди часто связывают их вместе.

Immutable.jsНа его создание у инженера Facebook Ли Байрона ушло 3 года, но он не был включен в набор инструментов React по умолчанию (React предоставляет упрощенный помощник). Он внутренне реализует полный наборPersistent Data Structure, а структуры данных и методы очень богатые (совсем не такие, как у JS). Например, Коллекция, Список, Карта, Набор, Запись, Последовательность. Есть очень полная карта, фильтр, groupBy, сокращение, найти функциональные методы работы. в то же время API также пытается быть похожим на Object или Array. Immutable.js загружает 16 КБ в сжатом виде.

Среди них есть 3 наиболее важные структуры данных для объяснения: (Java-программисты должны быть наиболее знакомы с ней)

  • Map: набор пар ключ-значение, соответствующий Object, ES6 также имеет специальный объект Map.

  • List: упорядоченный повторяющийся список, соответствующий массиву

  • Set: Беспорядочный и неповторимый список

Простой пример


import { Map } from "immutable";
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50

seamless-immutable

seamless-immutableэто еще один набор библиотек постоянных структур данных, он не реализует полныйPersistent Data Structure, вместо этого используйтеObject.defineProperty(Поэтому его можно использовать только в IE9 и выше) Он реализован путем расширения объектов JavaScript Array и Object и поддерживает только Array и Object два типа данных, API основан на Array и Object, поэтому многим не нужно менять свои привычки использования, вторжение в код очень мало. При этом его кодовая база тоже очень мала, а загрузка после сжатия составляет всего 2К.

Простой пример

// 使用  seamless-immutable.js 后
import Immutable from 'seamless-immutable';
var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]);

array[1] = "I'm going to mutate you!"
array[1] // "immutable"

array[2].hammer = "hm, surely I can mutate this nested object..."
array[2].hammer // "Can’t Touch This"

for (var index in array) { console.log(array[index]); }
// "totally"
// "immutable"
// { hammer: 'Can’t Touch This' }

JSON.stringify(array) // '["totally","immutable",{"hammer":"Can’t Touch This"}]'

seamless-immutableреализация зависит отECMAScript 5 некоторые функции, такие какObject.definePropertyа такжеObject.freeze, поэтому отсутствует совместимость с браузером:

image

Но это не проблема, можно использовать полифиллes-shims/es5-shimрешать.

В сравнении

несмотря на то чтоImmutable.jsПопробуйте сделать нативные объекты дизайна API похожими, иногда все равно сложно различить, является ли это неизменяемым объектом или нативным объектом, из-за чего легко перепутать операцию.

Хотя Map и List в Immutable соответствуют нативным Object и Array, их операции сильно различаются.Например, если вы хотите использоватьmap.get('key')вместоmap.key,array.get(0)вместоarray[0]. Кроме того, Immutable возвращает новый объект при каждом его изменении, и присваивание легко забыть.

При использовании внешних библиотек обычно необходимо использовать нативные объекты, и легко забыть о приведении типов.

Конечно, есть несколько способов избежать подобных проблем:

  • Используйте такие инструменты, как Flow или TypeScript, со статической проверкой типов.

  • Переменные соглашения о именовании: неизменно в виде всех типов объектов, которые начинаются с?

  • использоватьImmutable.fromJSвместоImmutable.MapилиImmutable.Listдля создания объектов, что позволяет избежать смешивания неизменяемых и собственных объектов.

Но есть и фатальная проблема: стоимость использования Immutable.js слишком велика, чтобы модифицировать существующий код.

а такжеseamless-immutableПока не так хорошо, как структура данных и APIImmutable.jsБогатый, но более чем достаточный для тех из нас, кто просто хочет использовать Immutable Data для оптимизации React, чтобы избежать повторного рендеринга. Более того, нативные методы Array и Object можно использовать напрямую, а исходные изменения проекта минимальны.

Используется в реакции

из-заseamless-immutableреализация зависит отECMAScript 5 Естественная совместимость с нативными Array и Object делает его использование в React очень простым, если вы обращаете внимание на три момента для достижения эффекта:

инициализировать состояние

При инициализации данных состояния используйте метод инициализации Immutable.

import Immutable from 'seamless-immutable';

state: {
    orderList: Immutable([]),
  }

Изменить данные о состоянии

При изменении данных состояния также обратите внимание на:

saveOrderList(state, {payload: items}) {
      return {...state, orderList: Immutable(items)};
    }

shouldComponentUpdate

использоватьpure-render-decorator, действительно удобно, быстро и элегантно. Конечно, поскольку декоратор — это функция ES7, Babel тоже нужно настраивать отдельно.

import React from 'react';
import pureRender from 'pure-render-decorator';

@pureRender
class OrderListView extends React.Component {
  render() {
    const {orderList} = this.props;
    return (
      <div>
        {
          orderList.map((item) => {
            return (
              <div key={item.orderNum}>
                <div>{item.orderNum}</div>
                <div>{item.createTime}</div>
                <div>{item.contact}</div>
                <hr/>
              </div>
            );
          })
        }
      </div>
    );
  }
}

export default OrderListView;

Как, оптимизация легендарного React SCU настолько проста, иди и попробуй.

Ссылаться на