1. Болевые точки
По нашему впечатлению,React
Кажется, это означает компонентизацию и высокую производительность.Нам всегда нужно заботиться только о данных в целом, а разница между двумя даннымиUI
Как изменить, то полностьюReact Virtual Dom
изDiff 算法
сделать это. Так что мы произвольно манипулируем данными и в основном оптимизируемshouldComponentUpdate
Мне лень писать, ведь можно и без записи правильно отрендерить. Однако по мере того, как размер приложения становится все больше и больше, вы обнаружите, что страница работает немного медленнее, особенно когда компоненты вложены больше, а структура данных более сложна, изменение элемента формы или выполнение фильтра на список будет потреблять много энергии.100ms
Выше, на этот раз нам нужно оптимизировать! Конечно, если вы не сталкиваетесь с узкими местами в производительности, не беспокойтесь, преждевременная оптимизация — это зло. Здесь мы суммируем очень простую схему, чтобы сделатьReact
Производительность приложения максимальна. В следующих разделах мы сначала рассмотрим некоторые базовые знания, в том числе:JavaScript
тип переменной иReact
Механизм рендеринга, если вы старый пташка, можете его сразу пропустить.
2. Обзор некоторых базовых знаний
1. Тип переменной
В JavaScript есть два типа переменных:
- Основные типы: 6 основных типов данных,
Undefined
,Null
,Boolean
,Number
,String
,Symbol
- Тип ссылки: совместно именуемый
Object
Тип, подразделяется на:Object
Типы,Array
Типы,Date
Типы,RegExp
Типы,Function
тип и т.д.
Например:
let p1 = { name: 'neo' };
let p2 = p1;
p2.name = 'dave';
console.log(p1.name); // dave
В ссылочном типе объявитеp1
объект, поставитьp1
назначить наp2
, в это время фактически присваивается адрес объекта в куче, а не данные в куче, то есть две переменные указывают на одно и то же место хранения, и следующиеp2.name
После изменения это также влияетp1
. Хотя это может сэкономить память, когда приложение сложное, вам нужно быть очень осторожным с данными, потому что, если вы не уделите внимание изменению значения одной переменной, это может повлиять на другую переменную. Если мы хотим, чтобы они не влияли друг на друга, нам нужно скопировать одни и те же данные.Копия делится на поверхностную копию и глубокую копию.Поверхностная копия будет копировать только данные первого слоя, а глубокая копия будет рекурсивно копировать все слои копирует, что потребляет больше производительности.
2. React
существуетReact
в, каждый разsetState
,Virtual DOM
вычислит два виртуальныхDOM
Разница между объектами, а затем изменение реального необходимо изменитьDOM
. из-заjs
Расчет быстрый, а операция реальнаяDOM
относительно медленный,Virtual DOM
Избегайте ненужной правдыDOM
операция, такReact
Производительность хорошая. Однако по мере увеличения сложности приложенияDOM
Деревья становятся все более сложными, и большое количество операций сравнения также может влиять на производительность. напримерTable
компонента, измените одну из строкTr
поле компонента,setState
после всех остальных строкTr
компоненты также будут выполняться один разrender
функция, которая на самом деле не нужна. мы можем пройтиshouldComponentUpdate
Функция решает, обновлять ли компонент. Большую часть времени мы можем знать, какие компоненты не изменятся, нет необходимости вычислять, какая часть виртуальногоDOM
.
3. Чистый компонент
React15.3
Добавлен новый класс вPureComponent, ранееPureRenderMixin
,а такжеComponent
В принципе то же самое, но вrender
Прежде чем помочь компоненту автоматически выполниться один разshallowEqual(поверхностное сравнение), чтобы решить, следует ли обновлять компонент, поверхностное сравнение похоже на поверхностное копирование, сравнивается только первый слой. использоватьPureComponent
Это равносильно пропуску записиshouldComponentUpdate
функция, когда компонент обновляется, если компонентprops
а такжеstate
:
- Ни эталон, ни данные первого уровня не изменились,
render
Метод не сработает, а это тот эффект, которого нам нужно добиться. - Хотя первый слой данных не изменился, но ссылка изменилась, это приведет к виртуальному
DOM
Вычислительные отходы. - Первый слой данных меняется, но ссылка не меняется, что не приведет к отрисовке, поэтому с данными нужно работать осторожно.
4. Неизменяемый.js
Immutable.jsдаFacebook
существует2014
Библиотека постоянных структур данных, созданных в течение года. Постоянство означает, что после создания данные не могут быть изменены. Любая операция модификации, добавления или удаления вернет новуюImmutable
объект. Это может облегчить нам решение таких проблем, как кэширование, откат, обнаружение изменения данных и т. д., и упростить разработку. и предоставляет большое количество подобных нативныхJS
метод иLazy Operation
функции, полнофункциональное программирование.
import { Map } from "immutable";
const map1 = Map({ a: { aa: 1 }, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1 !== map2; // true
map1.get('b'); // 2
map2.get('b'); // 50
map1.get('a') === map2.get('a'); // true
можно увидеть, изменитьmap1
недвижимость возвращаетсяmap2
, они не указывают на одно и то же место хранения,map1
Он объявлен только, и все операции его не изменят.
ImmutableJS
Предоставляет большое количество методов для обновления, удаления и добавления данных, что значительно облегчает нам работу с данными. Кроме того, родные типы иImmutableJS
Методы определения типа и преобразования:
import { fromJS, isImmutable } from "immutable";
const obj = fromJS({
a: 'test',
b: [1, 2, 4]
}); // 支持混合类型
isImmutable(obj); // true
obj.size(); // 2
const obj1 = obj.toJS(); // 转换成原生 `js` 类型
ImmutableJS
Две самые большие особенности:immutable data structures
(постоянная структура данных) сstructural sharing
(Совместное использование структуры), постоянная структура данных гарантирует, что данные не могут быть изменены после их создания.При использовании старых данных для создания новых данных старые данные не изменятся и не будут похожи на исходныеjs
Таким образом, операции с новыми данными повлияют на старые данные. Структурное совместное использование означает, что данные, которые не изменились, имеют общую ссылку, что не только снижает потребление производительности при глубоком копировании, но и уменьшает объем памяти. Например, следующая картинка:
Слева — старое значение, справа — новое значение, мне нужно изменить значение красного узла слева, сгенерированное новое значение изменяет все узлы между красным узлом и корневым узлом, то есть значение из всех голубых узлов старое значение ничего не меняет, другие места, где оно используется, не будут затронуты, и более половины синих узлов по-прежнему используются совместно со старым значением. существуетImmutableJS
Внутри создается специальная структура данных, объединяющая нативные значения с рядом частных свойств для созданияImmutableJS
Тип, каждый раз, когда значение изменяется, он сначала проходит вспомогательное обнаружение частного свойства, затем изменяет соответствующее частное свойство и реальное значение, которое необходимо изменить, и, наконец, генерирует новое значение. средний, поэтому производительность будет очень высокой.
5. Кейсы
Сначала мы рассмотрим использование толькоReact
В случае, почему производительность приложения тратится впустую, адрес кода:GitHub.com/Worryless/Share-отвратительно…, в этом случае используетсяcreate-react-app
, инструмент обнаружения используетchrome
Плагин:React Perf. воплощать в жизнь
git clone https://github.com/wulv/fe-example.git
cd fe-example/react-table
yarn
yarn start
Вы можете открыть страницу, начать запись, затем изменить столбец данных по желанию и закончить запись.Вы можете видеть, что мы изменили только одну строку данных, но вPrint Wasted
В этом элементе рендерингTr
Компонент потрачен впустую 5 раз:
Будь то операция добавления или удаления, она будет потрачена впустую.
n-1
второсортныйrender
,потому чтоApp
весь компонентstate
Изменилось, все компоненты будут перевоспитаны один раз, и, наконец, сравнение должно быть реальнымDOM
операция. мы кладемTable
компоненты иTr
унаследовалComponent
изменить наPureComponent
,Так,Tr
Каждый раз, когда компонент обновляется, это будет сделано один разshallowEqual
Для сравнения, после записи один раз вы обнаружите, что операция модификации не тратится впустую, но на этот раз операции добавления и удаления недействительны, а анализ добавленных операций:
add = () => {
const { data } = this.state;
data.push(dataGenerate())
this.setState({
data
})
}
data.push
не изменилсяdata
цитаты, такPureComponent
изshallowEqual
вернулся сразуtrue
, не ходиrender
. Это не то, что нам нужно, поэтому, если мы используемComponent
Определенно приведет к потерям производительности, используйтеPureComponent
Также необходимо убедиться, что при необходимости обновления компонентовprops
илиstate
Возвращает новую ссылку, иначе она не будет обновленаUI
.
В настоящее время,ImmutableJS
может показать свою силу, потому что гарантирует, что каждая модификация возвращает новыйObject
, давайте посмотрим на модифицированный пример: адрес кода:GitHub.com/Worryless/Share-отвратительно…, выполните ту же операцию, что и в приведенном выше примере, вы увидите:
Добавляйте, удаляйте, изменяйте операции, ни одной траты. Причина отсутствия отходов заключается в том, что все дочерние компоненты используют
PureComponent
,ImmutableJS
Операция модификации гарантированно возвращает новую ссылку, и изменяются только те узлы, которые необходимо изменить (PureComponent
могут быть отображены новые изменения), ссылки на другие узлы остаются неизменными (PureComponent
напрямую без рендеринга). Как можно заметить,PureComponent
а такжеImmutableJS
Это естественная пара, если их объединитьredux
, было бы совершеннее. потому чтоredux
изreducer
должен каждый раз возвращать новую ссылку, иногда нам приходится использоватьclone
илиassign
и т. д., чтобы гарантировать возврат новой ссылки, используйтеImmutanleJS
Естественно гарантирует это, нет необходимости вообщеlodash
Дождитесь библиотеки функций, например, я используюredux + immutable + react-router + express
Написал немного более сложный пример:GitHub.com/Worryless/Share-отвратительно… pageIndex
изstore
Статус:
{
loading: false,
tableData: [{
"name": "gyu3w0oa5zggkanciclhm2t9",
"age": 64,
"height": 121,
"width": 71,
"hobby": {
"movie": {
"name": "zrah6zrvm9e512qt4typhkt9",
"director": "t1c69z1vd4em1lh747dp9zfr"
}
}
}],
totle: 0
}
Если мне нужна быстрая модификацияwidth
Значение 90, сравните использование глубокого копирования,Object.assign
а такжеImmutableJS
Разница между тремя способами:
// payload = { name: 'gyu3w0oa5zggkanciclhm2t9', width: 90 }
// 1. 使用深拷贝
updateWidth(state, payload) {
const newState = deepClone(state);
return newState.tableData.map(item => {
if (tem.name === payload.name) {
item.width = payload.width;
}
return item;
});
}
// 2. 使用Object.assign
updateWidth(state, payload) {
return Object.assign({}, state, {
tableData: state.state.map(item => {
if (item.name === payload.name) {
return Object.assign({}, item, { width: payload.width });
}
return item;
})
})
}
// 3. 使用ImmutableJS
updateWidth(state, payload) {
return state.update('tableData', list => list.update(
list.findIndex((item) => item.get('name') === payload.name),
item => item.set('width', payload.width)));
}
Использование глубокой копии — дорогостоящая операция, и ссылки изменяются, что неизбежно приводит кre-render
, а такжеObject.assign
будет поверхностно копировать первый слой, хотя это не вызоветre-render
, а вот мелкая копия один раз копирует все остальные атрибуты, что тут тоже очень не нужно, только использоватьImmutableJS
Модификации выполняются идеально с минимальным кодом.
6. Преимущества и недостатки
Как можно заметить,ImmutableJS
комбинироватьPureComponent
приложение может быть значительно сокращеноre-render
Количество раз может значительно улучшить производительность. Но есть еще некоторые недостатки:
- Чтобы получить свойства компонента, вы должны использовать
get
илиgetIn
операции (кромеRecord
типа), такой и родной.
Операция гораздо более хлопотная, и если компонент был написан ранее, он нуждается в значительной модификации. -
ImmutableJS
Объем библиотеки относительно большой, около 56к, открытыйgzip
16k после сжатия. - стоимость обучения.
- трудно отлаживать, в
redux-logger
который должен быть вstateTransformer
Выполнить в конфигурацииstate.toJS()
.
7. Лучшие практики
На самом деле важно то, что программисты должны иметь представление об оптимизации производительности, знакомое сjs
Характеристики ссылочных типов, понимание природы вещей важнее, чем использование определенного фреймворка или библиотеки. Это также может быть достигнуто другими методамиImmutableJS
эффектов, таких как добавление данных, можно использовать оператор деструктуризации:
add = () => {
const { data } = this.state;
this.setState({
data: [...data, dataGenerate()]
})
}
Однако, если данные глубоко вложены, их запись еще более проблематична. Вот несколько советов:
- Есть также две облегченные библиотеки, реализующие неизменяемые структуры данных:seamless-immutableилиimmutability-helper, но принцип совсем другой, да и КПД не такой высокий.
- Избегайте интенсивного использования
toJS
операция, которая приводит к потере производительности. - не делай простым
JavaScript
объект сImmutable.JS
смешивание - комбинировать
redux
когда используешьimport { combineReducers } from 'redux-immutablejs';
,потому чтоredux
изcombineReducers
ожидатьstate
чистыйjs
объект. - как можно больше
state
Разработан, чтобы быть плоским. - Компоненты дисплея не используют
Immutable
структура данных. - Уходите
render
один в функцииPureComponent
компонентprops
использоватьbind(this)
илиstyle={ { width: '100px' } }
,потому чтоshallowEqual
Сравнение точно не пройдет.
8. Справочные ссылки
- Immutable.js, persistent data structures and structural sharing
- immutable.js is much faster than native javascript
- Подробное объяснение Immutable и практика в React
Эта статья была впервые опубликована вБлог о технологиях Youzan.