предисловие
Эта статья является ссылкой на книги с открытым исходным кодомReact In-depth: An exploration of UI developmentиндукция и усиление. В то же время, он также включает в себя часть моего собственного опыта в разработке.
Вы можете спросить, прочитав эту статью, полезно ли разрабатывать проекты, связанные с React, на работе? Честно говоря, это мало поможет. Эта статья не научит вас использовать новую технологию, она не поможет вам улучшить свои навыки программирования, но улучшит ваши знания React, такие как различение определенных концепций, понимание того, откуда берутся некоторые лучшие практики и т. д. Если вы настаиваете на том, чтобы рассматривать ценность этих знаний с утилитарной точки зрения, это будет очень полезно для вашего интервью.Точки знаний в этой статье часто спрашивают во время интервью, почему я знаю, потому что я съел их потерю .
Жизненный цикл компонентов React делится на рождение (монтирование), обновление (обновление) и смерть (размонтирование), но как узнать, на какой стадии находится компонент? Это можно узнать только через хуки-функции, предоставляемые нам компонентом React. Что такое хук-функция, так это функция, которая выполняется на определенном этапе. Например, конструктор будет вызываться только один раз на этапе рождения компонента, который считается «хуком». И наоборот, когда функция ловушки вызывается, это означает, что она вступила в определенный этап жизни, поэтому вы можете добавить некоторую логику кода в функцию ловушки для выполнения на определенной стадии. Конечно, это не абсолютно, например, функция рендеринга будет выполняться как на этапе рождения, так и на этапе обновления. Кстати, «хуки» также являются типом шаблона проектирования в программировании, например веб-хуки на github. Как следует из названия, это тоже хук, вы можете подписаться на события на github через Webhook, когда произойдет событие, github отправит POST-запрос на ваш сервис. С помощью этой функции вы можете прослушивать основную ветвь для нового события слияния, и если ваша служба получает сообщение об этом событии, вы можете, например, выполнить работу по развертыванию.
Мы объясняем каждую функцию крючка в хронологическом порядке этапов.
Пожалуйста, обратитесь к предыдущей статье для этапов рождения«Подробный жизненный цикл React (Часть 1): Стадия рождения (крепление)»
фаза обновления
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
Фаза обновления запускается в трех случаях:
-
Изменять
props
: компонент не изменяет активно то, чем он владеетprops
свойства, егоprops
Свойства передаются ему его родительским компонентом. Силовая параprops
Выполнение переназначения приведет к тому, что программа сообщит об ошибке. -
Изменять
state
:state
изменения вносятсяsetState
реализован интерфейс. Синхронный дизайнstate
Он требует навыков, какие состояния в него можно помещать, а какие нельзя, какие компоненты могут иметьstate
, которые не разрешены; все они должны следовать определенным принципам. Есть шанс, что эту тему можно будет поднять самостоятельно -
передача
forceUpdate
Метод: мы упоминали об этом на предыдущем этапе, заставляя компонент обновляться.
setState
асинхронный
Большая часть причины обновления компонента связана с вызовомsetState
Обновление интерфейсаstate
В результате мы часто вызываем синхронноsetState
,Но по фактуsetState
Метод асинхронный. Например следующий код:
onClick() {
this.setState({
count: 1,
});
console.log(this.state.count)
}
В обработчике события щелчка компонента мы обновляемstate
серединаcount
, то сразу попробуйте прочитать последнююcount
. Дело в том, что то, что вы читаете, не1
, два должно быть предыдущим значением.
Более фатальной ошибкой является последовательный вызов одного и того же уровня блока, как здесь.setState
код
this.setState({ ...this.state, foo: 42 });
this.setState({ ...this.state, isBar: true });
В этом случае первый наборfoo
Значение будет перезаписано второй настройкой и восстановлено.
componentWillReceiveProps(nextProps)
при передаче компонентуprops
Когда происходит изменение, компонентcomponentWillReceiveProps
То есть вызов будет инициирован, а параметры, переданные методом, будут после внесения изменений.props
значение (обычно мы называемnextProps
). В этом методе вы можете пройтиthis.props
Чтобы получить доступ к текущему значению свойства, вы можете передатьnextProps
Получите доступ к значениям свойств, которые должны быть обновлены, или сравните их, или вычислите их, чтобы определить состояние, которое вам нужно обновить (state
) и, наконец, вызовsetState
Методы обновления состояния. В этом вызове функции крюкаsetState
метод не запускает другой рендеринг.
Очень интересно, что хотяprops
изменения вызовутcomponentWillReceiveProps
звонок; ноcomponentWillReceiveProps
Звонок не означаетprops
Действительно изменился. Это не то, что я сказал, официальный представитель Facebook потратил целую статью, говоря об этом:(A => B) !=> (B => A). Например, посмотрите на следующий компонент:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 1,
}
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState({
number: 1,
})
}
render() {
return (
<MyButton onClick={this.onClick} data-number={this.state.number} />
);
}
}
Каждое событие клика используется повторноsetState
интерфейсная параstate
сделать обновление, но значение каждого обновления одинаково, т.е.number:1
. И передайте состояние текущего компонента как свойство<MyButton />
. Вот проблема, то каждый раз, когда я нажимаю кнопку, кнопкаMyButton
изcomponentWillReceiveProps
будут называть?
Да, даже если значение одинаково при каждом обновлении.
Причина этого на самом деле очень проста, потому что React не знает, изменилось ли переданное свойство. И почему React не пытается выполнить проверку на равенство?
Поскольку это невозможно, вновь переданный атрибут и старый атрибут могут ссылаться на одну и ту же область памяти (ссылочный тип), поэтому просто используйте===
Определение равенства не является точным. Одним из возможных решений является создание глубокой копии данных и последующее сравнение, но это слишком плохо для больших структур данных и может привести к проблеме циклических ссылок.
Так что React раскрывает это изменение через функцию ловушки, не думайте, что когдаcomponentWillReceiveProps
называться значитprops
Произошло изменение, и если вам нужно что-то сделать с этим изменением, обязательно выполните сравнение вручную.
shouldComponentUpdate()
shouldComponentUpdate
Важно, что он может решить, продолжать ли текущий жизненный цикл. По умолчанию функция возвращаетtrue
То есть продолжить текущий жизненный цикл, также можно вернутьсяfalse
Завершает текущий жизненный цикл, предотвращая дальнейшееrender
со следующими шагами.
Как мы только что сказали выше, React неprops
Для более глубокого сравнения эта параstate
То же самое применимо. так что даже еслиprops
а такжеstate
не изменился,shouldComponentUpdate
также будет вызван снова, включая следующие шагиcomponentWillUpdate
,render
,componentDidUpdate
также будет работать снова. Очевидно, что это сильно снижает производительность.
Перейти кshouldComponentUpdate
Параметры включают предстоящие измененияprops
а такжеstate
, имя параметраnextProps
а такжеnextState
, в этой функции вы также можете передатьthis
ключевое слово для доступа к текущемуstate
а такжеprops
, так что тут вы "всезнайка" и можете судить о том полностью или нет исходя из собственной бизнес логикиstate
а такжеprops
произошло ли изменение, и решить, следует ли продолжать следующие шаги.shouldComponentUpdate
Обычно это наш первый шаг при оптимизации производительности React. Оптимизация этого шага не только оптимизирует процесс самого компонента, но и экономит затраты на повторный рендеринг подкомпонентов.
Конечно, если судитьprops
Если логика обнаружения того, произошло ли изменение, относительно проста, например, это только поверхностное суждение (то есть, изменилась ли ссылка на объект), изменился ли объект, тогда вы можете использоватьPureRenderMixin
:
import PureRenderMixin from 'react-addons-pure-render-mixin'; // ES6
const createReactClass = require('create-react-class');
createReactClass({
mixins: [PureRenderMixin],
render: function() {
return <div className={this.props.className}>foo</div>;
}
});
minins
Это механизм, поддерживаемый React, который позволяет нескольким компонентам совместно использовать код.PureRenderMixin
Работа плагина очень проста, он переписывает ее за васshouldComponentUpdate
функцию и неглубокое сравнение объектов, конкретный код можно найти изздесьа такжездесьоказаться.
В ES6 вы также можете использовать прямое наследованиеReact.PureComponent
вместоReact.Component
для достижения этой функции. По официальным словам React, это
React.PureComponent
is exactly likeReact.Component
, but implementsshouldComponentUpdate()
with a shallow prop and state comparison.
Pure
Подчеркнем еще раз,PureComponent
Все, что реализовано за вас, это определить, изменилась ли ссылка, и даже можно сказать, что она просто используется===
суждения сделаны, так что это то, что мы называемpureпричина. Чтобы проиллюстрировать проблему конкретно, давайте возьмем практический пример.
/* MyButton.js: */
import React from 'react';
class MyButton extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
console.log('render');
return <button onClick={this.props.onClick}>My Button</button>
}
}
export default MyButton;
/* App.js: */
import React from 'react';
import MyButton from './Button.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
arr: [1],
}
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState({
arr: [...this.state.arr, 2],
});
}
render() {
return (
<MyButton onClick={this.onClick} data-arr={this.state.arr} />
);
}
}
export default App;
В приведенном выше примере каждый щелчок будет изменятьstate
серединаarr
Переменная,arr
И ссылка, и значение переменной изменились. Дело в томMyButton
Компоненты наследуются отReact.PureComponent
. Затем каждый раз, когда вы нажимаете,MyButton
Информация о журнале будет распечатана, то есть каждый раз будет начинаться заново.render
если мы положимonClick
Метод с некоторыми изменениями:
onClick() {
const arr = this.state.arr;
arr.push(2);
this.setState({
arr: arr,
})
}
Этот метод также делаетarr
Переменная изменилась, но только значение, а не ссылка, и при повторном нажатии кнопки (MyButton
)Время,MyButton
не будет рендериться снова. то естьPureComponent
Неглубокое сравнение было сделано для нас заранее.
Использование неизменяемых данных, которые изменяют только ссылки и не изменяют содержимое данных, часто используется как одно из средств оптимизации React.immutable.jsЭто может быть достигнуто для нас.Каждый раз, когда вы изменяете данные, вы фактически получаете новую ссылку на данные без изменения исходных данных. При этом эффект, которого хочет добиться редьюсер в Redux, на самом деле похож.reducer
Ключевым моментом является его чистота (pure), которая не вызовет побочных эффектов при выполнении, то есть позволит избежать модификации входящих ссылок на данные, а также облегчит сравнение обновлений состояния компонентов.
componentWillUpdate()
componentWillUpdate
Методы иcomponentWillMount
Методы очень похожи, все они запускаются непосредственно перед рендерингом, здесь вы можете получитьnextProps
а такжеnextState
, а также может получить доступ к текущему истекающемуprops
а такжеstate
. Вы можете временно сохранить их для последующего использования, если это необходимо.
а такжеcomponentWillMount
Отличие в том, что в этом методе нельзя использоватьsetState
, в противном случае он немедленно вызовет еще один раунд рендеринга и повторный вызовcomponentWillUpdate
, в бесконечном цикле.
componentDidUpdate()
Аналогично этапу монтирования, когда компонент входитcomponentDidUpdate
Стадия означает, что последняя нативная DOM была визуализирована и доступна черезrefs
получить доступ. Эта функция будет передавать два параметра, которыеprevProps
а такжеprevState
, как следует из названия, является предыдущим состоянием. ты еще можешь пройтиthis
Ключевое слово обращается к текущему состоянию, потому что оно может обращаться к отношениям родной модели DOM, а также подходит для некоторых сторонних операций, которым необходимо манипулировать библиотекой классов.
Последовательность вызова каждой функции ловушки на этапе обновления также аналогична последовательности вызова на этапе монтирования, особенноcomponentDidUpdate
, функция ловушки дочернего компонента вызывается перед родительским компонентом
Поскольку мы можем получить доступ к DOM, нам может понадобиться получить фактический стиль элемента в этой функции ловушки и написатьstate
В этом, например, ваш код может расти следующим образом:
componentDidUpdate(prevProps, prevState) {
// BAD: DO NOT DO THIS!!!
let height = ReactDOM.findDOMNode(this).offsetHeight;
this.setState({ internalHeight: height });
}
Если ваш по умолчаниюshouldComponentUpdate()
функция всегда возвращаетtrue
Если так, тоcomponentDidUpdate
Обновитьstate
код снова уносит нас в бесконечностьrender
в цикле. Если вы должны это сделать, то хотя бы кешируйте последний результат и обновляйте условноstate
:
componentDidUpdate(prevProps, prevState) {
// One possible fix...
let height = ReactDOM.findDOMNode(this).offsetHeight;
if (this.state.height !== height ) {
this.setState({ internalHeight: height });
}
}
стадия смерти
componentWillUnmount()
Эта функция ловушки срабатывает, когда компонент необходимо удалить из DOM. Здесь не так много нужно знать, в этой функции обычно выполняется некоторая работа, связанная с «очисткой».
- Отменить все отправленные сетевые запросы
- Удалите прослушиватель событий из DOM компонента.
Суммировать
Наконец, еще раз подчеркивается, что эта статья является книгой с открытым исходным кодом.React In-depth: An exploration of UI developmentиндукция. В принципе, достаточно прочитать эту книгу, если вы хотите понять жизненный цикл, и после прочтения вы непобедимы. Надеюсь, эта упрощенная китайская версия также будет вам полезна.
Эта статья также была опубликована в моемЗнай колонку, приветствую всех, чтобы обратить внимание