Об авторе Эми Ант Технологическая команда Financial Data Experience
В последнее время, в процессе развития спроса, я наступил на много ям, потому что справочные данные были обновлены, но страница не была перерисована, поэтому я резюмировал содержание этой части.
Прежде чем говорить о неизменяемых данных, давайте поговорим о том, как компоненты React отображаются и обновляются.
Как обновляются компоненты React
прямые изменения состояния
Обновление компонента React вызвано изменением состояния компонента.Состояние здесь обычно относится к объекту состояния в компоненте.Когда состояние компонента изменяется, компонент проходит следующий процесс, когда он обновлено:
- shouldComponentUpdate
- componentWillUpdate
- render()
- componentDidUpdate
Обновление состояния обычно выполняется путем выполнения операции this.setState внутри компонента, но setState является асинхронной операцией, она выполняет только изменяемое состояние и помещает его в очередь выполнения React будет комбинировать несколько операций setState для повышения производительности. причины Выполнить один раз.
Изменения в реквизите
В дополнение к тому, что состояние заставит компонент обновляться, реквизиты, переданные извне, также будут обновлять компонент, но это когда дочерний компонент напрямую использует реквизиты родительского компонента для рендеринга, например:
render(){
return <span>{this.props.text}</span>
}
Когда свойства обновляются, дочерний компонент будет отображать обновление в следующем порядке:
- componentWillReceiveProps (nextProps)
- static getDerivedStateFromProps()
- shouldComponentUpdate
- componentWillUpdate
- render
- getSnapshotBeforeUpdate()
- componentDidUpdate
образец кодаНа основе вывода в примере можно сразу увидеть порядок, в котором выполняется жизненный цикл компонента React.
косвенные изменения состояния
Другим является преобразование реквизита в состояние для рендеринга компонента.В настоящее время, если реквизиты обновлены, для повторного рендеринга компонента вам необходимо назначить последние реквизиты состоянию в жизненном цикле componentWillReceiveProps, например:
class Example extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
text: props.text
};
}
componentWillReceiveProps(nextProps) {
this.setState({text: nextProps.text});
}
render() {
return <div>{this.state.text}</div>
}
}
Обновление в этом случае также является вариантом setState, но источник состояния другой.
Процесс обновления компонентов React
Когда компонент React обновляется (состояние или реквизиты меняются), React построит новое дерево Virtual DOM на основе нового состояния, а затем использует алгоритм сравнения для сравнения этого виртуального DOM с предыдущим виртуальным DOM, если отличается, затем повторно визуализирует . React вызовет функцию shouldComponentUpdate перед рендерингом, чтобы узнать, нужно ли его повторно рендерить.Для анализа исходного кода всей ссылки см.здесь, Возвращаемое по умолчанию значение функции shouldComponentUpdate в React равно true, поэтому, если какая-либо позиция в компоненте изменится, другие неизмененные части компонента также будут повторно визуализированы.
Когда механизм рендеринга компонента очень прост, эффект от изменения всего компонента за счет изменения состояния может быть невелик, но когда рендеринг компонента очень сложный, например древовидный компонент со множеством узлов, при изменении определенного компонент Когда достигается состояние листового узла, все дерево будет повторно отображаться, даже те узлы, состояние которых не было обновлено, что в некоторой степени снижает производительность, вызывая замедление скорости рендеринга и обновления всего компонента. , что влияет на пользовательский опыт.
Поверхностное сравнение PureComponent
Основываясь на упомянутых выше проблемах с производительностью, React представил PureComponent, функции которого аналогичны подключаемому модулю PureRenderMixin.метод, Этот метод в основном выполняет поверхностное сравнение, код выглядит следующим образом:
function shallowCompare(instance, nextProps, nextState) {
return (
!shallowEqual(instance.props, nextProps) ||
!shallowEqual(instance.state, nextState)
);
}
Логика PureComponent для определения необходимости обновления такая же, как и у плагина PureRenderMixin.исходный кодследующим образом:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate =
!shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState);
}
Хотя использование двух вышеуказанных методов позволяет избежать ненужного повторного рендеринга элементов, которые не изменились, использование описанного выше поверхностного сравнения все же создает некоторые проблемы:
Если структура данных реквизита, переданного компоненту, выглядит следующим образом:
const data = {
list: [{
name: 'aaa',
sex: 'man'
},{
name: 'bbb',
sex: 'woman'
}],
status: true,
}
Поскольку приведенные выше данные данных являются ссылочным типом, когда одно из полей изменяется и ожидается, что компонент может быть повторно отображен после изменения, обнаружено, что при использовании PureComponent обнаруживается, что компонент не перерисовывается, потому что измененные данные и та же память, используемая данными до модификации, результаты всех сравнений всегда ложны, в результате чего компонент не перерисовывается.
Несколько способов решения проблемы
Чтобы решить вышеуказанную проблему, необходимо рассмотреть, как реализовать несоответствие между обновленными эталонными данными и памятью, на которую указывают исходные данные, то есть использовать неизменяемые данные.Следующие методы суммируются сами по себе;
Глубокое копирование с использованием lodash
Код реализации этого метода выглядит следующим образом:
import _ from "lodash";
const data = {
list: [{
name: 'aaa',
sex: 'man'
}, {
name: 'bbb',
sex: 'woman'
}],
status: true,
}
const newData = _.cloneDeepWith(data);
shallowEqual(data, newData) //false
//更改其中的某个字段再比较
newData.list[0].name = 'ccc';
shallowEqual(data.list, newData.list) //false
Этот метод заключается в том, чтобы сначала глубоко скопировать сложные типы, а затем изменить одно из значений, чтобы они использовали разные ссылочные адреса и, естественно, возвращали false при сравнении, но есть недостаток, заключающийся в том, что реализация этой глубокой копии будет потреблять много памяти.
Используйте JSON.stringify()
Этот метод приравнивается к разновидности черной магии и применяется следующим образом:
const data = {
list: [{
name: 'aaa',
sex: 'man'
}, {
name: 'bbb',
sex: 'woman'
}],
status: true,
c: function(){
console.log('aaa')
}
}
const newData = JSON.parse(JSON.stringify(data))
shallowEqual(data, newData) //false
//更改其中的某个字段再比较
newData.list[0].name = 'ccc';
shallowEqual(data.list, newData.list) //false
Этот метод фактически является вариантом глубокого копирования.Помимо тех же недостатков, что и выше, есть еще два момента, что если у вашего объекта есть функция, то функция не может быть скопирована, и объект copyObj не может быть скопирован одновременно , Свойства и методы в цепочке прототипов
Деструктуризация с помощью объекта
Деструктуризация объекта — это синтаксис ES6.Давайте сначала проанализируем предыдущий код:
const data = {
list: [{
name: 'aaa',
sex: 'man'
}, {
name: 'bbb',
sex: 'woman'
}],
status: true,
}
const newData = {...data};
console.log(shallowEqual(data, newData)); //false
console.log(shallowEqual(data, newData)); //true
//添加一个字段
newData.status = false;
console.log(shallowEqual(data, newData)); //false
//修改复杂类型的某个字段
newData.list[0].name = 'abbb';
console.log(shallowEqual(data, newData)); //true
С помощью вышеуказанного теста можно обнаружить, что: При модификации переменных простых типов в данных использование деструктуризации может решить проблему, но при модификации в ней сложных типов ее не обнаружить (однажды я наступил на большую дыру).
Потому что деструктурирование — это Object.assign() после компиляции babel, но это поверхностная копия, которая представлена следующим образом:
Недостаток этого метода очевиден, для сложных типов данных невозможно обнаружить его обновление.
Используйте сторонние библиотеки
Промышленность предоставляет некоторые библиотеки для решения этой проблемы, такие какimmutability-helper , immutableилиimmutability-helper-x.
immutability-helper
Библиотека, основанная на операциях с массивами и объектами, всего в одном файле, но очень удобная в использовании. Например, приведенный выше пример можно записать следующим образом:
import update from 'immutability-helper';
const data = {
list: [{
name: 'aaa',
sex: 'man'
}, {
name: 'bbb',
sex: 'woman'
}],
status: true,
}
const newData = update(data, { list: { 0: { name: { $set: "bbb" } } } });
console.log(this.shallowEqual(data, newData)); //false
//当只发生如下改变时
const newData = update(data,{status:{$set: false}});
console.log(this.shallowEqual(data, newData)); //false
console.log(this.shallowEqual(data.list, newData.list)); //true
В то же время можно обнаружить, что когда изменяется только поле состояния в данных, сравниваются опорные поля двух до и после, и обнаруживается, что память является общей, что в определенной степени экономит потребление памяти. . И API знаком с некоторыми операциями над массивами и объектами, которые относительно просты в использовании.
immutable
По сравнению с immutability-helper, immutable гораздо мощнее, но в то же время и увеличивает стоимость обучения, потому что новые API нужно изучать.Поскольку они мало использовались, не будем здесь повторяться. определенные точки знаний могут быть перемещены.здесь.
immutability-helper-x
Наконец, рекомендация другой базы данных с открытым исходным кодомimmutability-helper-x, API лучше использовать ~ Вы можете использовать
const newData = update(data, { list: { 0: { name: { $set: "bbb" } } } });
Упростите до чего-то более читабельного
const newData = update.$set(data, 'list.0.name', "bbb");
或者
const newData = update.$set(data, ['list', '0', 'name'], "bbb");
напиши в конце
В проектах React все же лучше использовать неизменяемые данные, что позволяет избежать многих непреднамеренных ошибок. Вышеизложенное является лишь некоторыми обобщениями и накоплениями личного развития в актуальном развитии.
Если вам интересна наша команда, вы можете подписаться на рубрику и подписатьсяgithubИли отправьте свое резюме на 'tao.qit####alibaba-inc.com'.replace('####', '@'), люди с высокими идеалами могут присоединиться~
Оригинальный адрес:GitHub.com/proto team/no…