Автор оригинала: Christian Alfoni.
Итак, в городе есть новая архитектура, поток, и он начинает получать какую-то твердую землю. Есть несколько реализаций, среди тех Facebook Flux , Yahoo Dispatchrı , FluxXor , Mcfly и JFLUX , который я лично работал. Один из Core ConcePS в потоке – магазины Отказ Магазин изменит событие изменения всякий раз, когда какое-либо из государства, проводимого в изменениях в магазине. Любые компоненты, слушающие изменения в магазинах, будут захватывать состояние из соответствующего магазина и рендера. В этой статье мы собираемся взглянуть на рендеринг React JS, когда он работает в традиционной среде потока.
Событие изменить
При чтении документации на архитектуре потока вы наверняка встретите «Изменить событие». В принципе всякий раз, когда магазин сделал изменение в какое-то состояние, он выделяет этот тип события, Изменить Отказ Имея только один тип события, которое ваша заявка будет, конечно, будет более управляемым с точки зрения сохранения всех ваших компонентов в синхронизации с магазинами, но она также имеет стоимость. Давайте посмотрим на пример с флюс-реагирование хранить:
var store = flux.createStore({ todos: [], addTodo: function (todo) { this.todos.push(todo); this.emit('change'); }, removeTodo: function (index) { this.todos.splice(index, 1); this.emit('change'); }, exports: { getTodos: function () { return this.todos; } } });
Любые компоненты, слушающие событие изменения в этом магазине, будут использовать gettodos Способ, когда TODOS добавляются и удалены, чтобы получить обновленное состояние. Это имеет смысл, и это абсолютно ничего плохого в этом, так как весьма вероятно, что любой компонент, заинтересованный в добавлении Тодоса, вероятно, также будет заинтересован в их удалении. Но давайте добавим другое состояние:
var store = flux.createStore({ todos: [], isSaving: false, addTodo: function (todo) { this.todos.push(todo); this.isSaving = true; this.emit('change'); doSomethingAsync().then(function () { this.isSaving = false; this.emit('change'); }.bind(this)); }, removeTodo: function (index) { this.todos.splice(index, 1); this.isSaving = true; this.emit('change'); doSomethingAsync().then(function () { this.isSaving = false; this.emit('change'); }.bind(this)); }, exports: { getTodos: function () { return this.todos; }, isSaving: function () { return this.isSaving; } } });
Теперь мы впервые запускаем изменения, чтобы уведомить наши компоненты, которые в магазине в Выдача Состояние и что у нас есть новый Todo. Позже мы снова уведомим наши компоненты о магазине, не экономите больше. С этим простым примером мы начинаем видеть, где растут стоимость. Вы можете утверждать, что не должны быть асинхронистыми операциями внутри магазинов, но несут со мной на этом. В этом случае мы сосредоточиваемся на использовании общего события «Изменить» как для уведомления о наших Выдача состояние и обновление нашего Тодос государственный. Давайте визуализируем то, что я имею в виду здесь. Представьте себе этот HTML быть компонентами:
Мы хотим вклад внутри
- Мы снимаем обоих Выдача и Тодос Когда компоненты созданы
- Мы добавляем новую тоду, вызывающее событие «Изменить» событие
-
хватает новое Выдача Состояние, отключение сама и хватает мутировать Тодос Состояние, чтобы показать новый Todo в списке - Когда операция ASYNC выполняется, мы вызываем новое событие изменений, вызывая наши два компонента, чтобы схватить то же состояние, хотя
На самом деле не пришлось, так как не было новых мутаций на массиве Тодоса
Без необходимости реагирования на государственные изменения не является единственной стоимостью, хотя позволяет выглядеть немного глубже.
Реагирование каскадных рендеров JS
Одна важная деталь о реагированных js, которые часто упускают из виду, это как SetState На компоненте влияет на вложенные компоненты. Когда вы используете SetState Это не только текущий компонент, который будет делать рендеринг, но и все вложенные компоненты. Это означает, что событие изменений слушают на вашем корневом компонентах приложения, и событие изменений инициируется из магазина, все ваши компоненты будут делать рендеринг и дифференцировать для получения любых необходимых операций DOM. Давайте визуализируем это:
[Cascading render] /---\ | X | - Root component renders |---| | /----|---\ | | /---\ /---\ | X | | X | - Nested components also renders |---| |---|
Но если вложенный компонент делает SetState Это не повлияет на родительские компоненты.
[Cascading render] |---| | | - Root component does not render |---| | /----|---\ | | /---\ /---\ | | | X | - Nested component renders |---| |---|
Это на самом деле означает, что вы можете сойти только с прослушиванием изменений в магазинах на вашем корневом компонентах, запуская STETSTATE, а затем просто схватить состояние непосредственно из магазинов в вложенных компонентах. Мы можем создать пример этого с нашим Todoapp:
var TodoApp = React.createClass({ componentWillMount: function () { AppStore.on('change', this.update); }, componentWillUnMount: function () { AppStore.off('change', this.update); }, update: function () { this.setState({}); // Just trigger a render }, render: function () { return (); } });
Этот компонент просто прослушивает общее событие изменения и запускает рендеринг. Как указано выше, это будет каскад до вложенных компонентов, поэтому, если мы F.Ex. в нашем
var AddTodoComponent = React.createClass({ render: function () { return (); } });
Это на самом деле все, что нам нужно для обработки состояния инвалидов. Не нужно слушать изменения, потому что наш корневой компонент делает это для нас.
Это дает вам предсказуемость в рендеринге вашего приложения, но есть, конечно, баланс. Позвольте сказать, что вы уже слушаете большинство мероприятий по изменению событий в корневом компоненте вашего приложения, это было бы ситуацией, когда вы можете рассмотреть возможность удаления дополнительных вложенных слушателей, поскольку они только приведут лишь ненужные рендеры. Но если у вас очень плоская компонентная структура, которая слушает многих разных магазинов, которые, конечно, не будут такими хорошей идеей. Давайте погрузиться немного глубже.
Повторное рендеринг
Общее изменение события, вызывающее A SetState На компонент не только приведет к каскадированному рендерингу, но также приведет к повторному рендурированию в компонентах. Позволь мне объяснить:
[Repeated rendering] /---\ | | - Root component listens to change |---| | /----|---\ | | /---\ /---\ | | | | - Nested components listens to change |---| |---|
Когда событие изменения теперь происходит корневой компонент, сначала срабатывает о рендере:
[Repeated rendering] /---\ | X | - Root component reacts to change event and renders |---| | /----|---\ | | /---\ /---\ | X | | X | - Nested components render |---| |---|
И после этого вложенные компоненты действительно сделают себя снова:
[Repeated rendering] /---\ | | - |---| | /----|---\ | | /---\ /---\ | X | | X | - Nested components react to change event and renders |---| |---|
Теперь, если были более глубокие вложенные компоненты, это приведет к тому же эффекту, но с еще более повторным рендерингом из-за каждого вложенного уровня вызывает дополнительный рендер.
Если это новое для вас, ваша реакция может быть в жанре «Yikes», но React js чрезвычайно быстрые, а в простых приложениях это вообще не проблема. Но если вы похожи на меня, вы хотите знать, как ваш код работает, и теперь вы делаете. Таким образом, давайте посмотрим на некоторые решения для оптимизации этого поведения.
Оптимизация
Сначала я хотел бы занять минуту, чтобы посмотреть на должен быть необходим метод. Это используется для контроля поведения каскадного рендеринга. Давайте снова посмотрим на нашу визуализацию:
[Cascading render] /---\ | X | - Root component renders |---| | /----|---\ | | /---\ /---\ | X | | X | - Nested components also renders |---| |---|
Если наши вложенные компоненты имели должен быть необходим метод, и это вернуло ложь
:
var NestedComponent = React.createClass({ shouldComponentUpdate: function () { return false; }, render: function () { return ( ); } });
Это будет результатом:
[Render cascading] /---\ | X | - Root component renders |---| | /----|---\ | | /---\ /---\ | | | | - Nested components do not render |---| |---|
Но это боль, чтобы добавить это ко всем вашим компонентам. То, что вы могли сделать вместо этого, это добавить микс из аддо-аддонов, называемых Purerendermixin Отказ Это будет иметь тот же эффект.
var NestedComponent = React.createClass({ mixins: [React.addons.PureRenderMixin], render: function () { return ( ); } });
Это, безусловно, улучшает производительность рендеринга, но все же каждый отдельный компонент, слушающий изменения, постарается сделать новый рендер, даже если произошло состояние, изменение компонента не заботилось. В дополнение к этому вы должны убедиться, что любой объект или массивы, установленные на ваших реквизитах или состоянии, изменят их ссылку при изменении. Это потому, что Purererendermixin только делает неглубокую проверку.
Предотвращение ненужных он
Одним из примеров управления рендурами является использование более одного события «Изменить». флюс-реагирование Использует Eventemitter2, чтобы разрешить мероприятиям пространства. Позвольте мне просто показать вам пример:
var TodosListComponent = React.createClass({ mixins: [flux.RenderMixin], componentWillMount: function () { AppStore.on('todos.*', this.changeState); }, componentWillUnmount: function () { AppStore.off('todos.*', this.changeState); }, changeState: function () { this.setState({ todos: AppStore.getTodos() }); }, render: function () { return (
-
{this.state.todos.map(function (todo) {
return
- {todo.title} })}
Флюс-реагирующий Rendermixin делает ту же работу, что и Purerendermixin , но теперь вам не нужно включать в себя addons addons, чтобы получить преимущества.
Как вы можете видеть, я использовал Asterix WildCard при прослушивании событий. Это на самом деле означает, что магазин испускает следующее событие при добавлении TODO, Это .emit (‘todos.add’) и, this.emit (‘todos.remove’) , при удалении тодо. Наш TodoslistComponent будет действовать на обоих мероприятиях. Какой-то другой компонент может быть заинтересован в добавлении TODO и, таким образом, только попробуйте еще раз рендерировать, когда случается определенное событие. Это, конечно, добавит сложность к вашему приложению, но вы получаете больше контроля о рендеринге, и вы также можете реагировать на переходы в состоянии, что является вызовом с традиционным потоком. Позволь мне объяснить.
Позвольте сказать, что у вас есть iFrame, управляемый компонентом, который должен обновить каждый раз, когда Todo добавляется … по любой причине. Как бы вы справились с этим событием «Изменить»? Возможно, вам придется упомянуть ссылку на количество TODOS внутри компонента, и на всех событиях изменений вам придется проверить длину, хранящуюся в компоненте с длиной из магазина. Это не хороший способ сделать это. При излучении события «TODOS.ADD», хотя вы знаете, что на самом деле добавлен TODO, и вы можете безопасно освежить iFrame в каждом появлении этого события.
Прослушивание определенных переходов состояния – это то, что требуется в современных приложениях по одной странице. Изменения маршрута, анимации и другие типы переходов очень сложно обращаться с одним «изменением» события.
Государственное дерево
Также возможно использовать дерево штата, как Baobab Отказ Лично я думаю, что это следующая эволюция архитектуры потока. Это решает так много проблем и сохраняет удивительно простое API. Давайте сначала посмотрим на пример:
var Baobab = require('baobab'); var stateTree = new Baobab({ todos: [] }, { // We add the PureRenderMixin to all our components using // a tree or cursor mixin mixins: [React.addons.PureRenderMixin], // When the tree updates it makes sure all objects and arrays // change their reference so PureRenderMixin can do its shallow // check shiftReferences: true }); var todosCursor = stateTree.select('todos'); var TodosListComponent = React.createClass({ // We add a mixin from our cursor which listens for // updates and triggers a render. The React.PureRenderMixin // is also included mixins: [todosCursor.mixin], // The data on the cursor is available on your state, via // the cursor property render: function () { return (
-
{this.state.cursor.map(function (todo) {
return
- {todo.title} })}
Baobab позволяет создать дерево штата, которое вы можете указать на курсорами. Вы можете прослушать изменения этих курсоров, как в примере выше, мы слушаем изменения на «TODOS». Это дает очень простое API с очень маленькой котельной, которая обрабатывает оптимизацию для вас. У Baobab есть несколько других трюков вверх по рукаве, который вмещает архитектуру потока, поэтому я рекомендую вам проверить это.
Резюме
Эта статья не намерена указывать на вас в «лучшую практику». Его намерение заключается в том, чтобы дать вам некоторое представление о том, как работает RACK READ REFERNED REFERNED и как реализация потока вы выберете, влияет на рендуринг JS. Чтобы округлить это, я просто хочу сказать, что я надеюсь, что эта статья внесла большую часть понимания того, как работает React JS. Flux по-прежнему очень новая концепция, и мы, вероятно, собираемся увидеть еще больше реализации в течение некоторого времени, но React JS определенно здесь, чтобы остаться.