Статьи

Введение в React Framework

В современном мире фреймворков Javascript Application философия дизайна является ключевым дифференцирующим фактором. Если вы сравните популярные JS-фреймворки, такие как EmberJS, AngularJS, Backbone, Knockout и т. Д., Вы обязательно найдете различия в их абстракциях, моделях мышления и, конечно, терминологии. Это прямое следствие основополагающей философии дизайна. Но, в принципе, все они делают одну вещь — абстрагировать DOM таким образом, чтобы вы не имели дело непосредственно с элементами HTML.

Я лично думаю, что фреймворк становится интересным, когда он предоставляет набор абстракций, которые позволяют использовать другой способ мышления. В этом аспекте, реагируйте , новая платформа JS от людей из Facebook заставит вас переосмыслить (до некоторой степени), как вы разлагаете пользовательский интерфейс и взаимодействия вашего приложения. Достигнув версии 0.4.1 (на момент написания этой статьи), React предоставляет удивительно простую, но эффективную модель для создания приложений JS, которая смешивает восхитительный коктейль другого рода.

В этой статье мы рассмотрим строительные блоки React и рассмотрим стиль мышления, который с первого взгляда может показаться нелогичным. Но, как говорят в React документы: «Дайте пять минут», и тогда вы увидите, как этот подход станет более естественным.

История React началась в пределах Facebook, где она варилась некоторое время. Достигнув достаточно стабильного состояния, разработчики решили открыть его несколько месяцев назад. Интересно, что сайт Instagram также работает на React Framework.

React подходит к проблеме абстракции DOM с немного другим подходом. Чтобы понять, чем это отличается, давайте быстро заглушим методы, принятые вышеупомянутыми платформами.

Шаблон проектирования MVC (Model-View-Controller) имеет основополагающее значение для разработки пользовательского интерфейса, причем не только в веб-приложениях, но и во внешних приложениях на любой платформе. В случае веб-приложений DOM — это физическое представление View. Сам DOM генерируется из текстового html-шаблона, который извлекается из другого файла, блока скрипта или предварительно скомпилированной функции шаблона. View — это сущность, которая воплощает текстовый шаблон в жизнь как фрагмент DOM. Он также устанавливает обработчики событий и заботится о манипулировании деревом DOM как части его жизненного цикла.

Чтобы View было полезным, необходимо показать некоторые данные и, возможно, разрешить взаимодействие с пользователем. Данные — это Model , которая поступает из некоторого источника данных (базы данных, веб-службы, локального хранилища и т. Д.). Фреймворки предоставляют способ «привязки» данных к представлению, так что изменения в данных автоматически отражаются при изменениях в представлении. Этот автоматический процесс называется привязкой данных, и есть API / методы, чтобы сделать его как можно более плавным.

Триада MVC завершается Controller , который включает View и Model и организует поток данных ( Model ) в View и пользовательские события из View , что может привести к изменениям в Model .

MVC-поток

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

С React, View-часть триады MVC занимает выдающееся положение и превращается в объект, называемый Component . Компонент поддерживает неизменяемый пакет свойств, называемый props , и state которое представляет управляемое пользователем состояние пользовательского интерфейса. Часть Component генерирующая представления, довольно интересна и, возможно, является причиной, по которой React выделяется на фоне других сред. Вместо создания физического DOM непосредственно из файла шаблона / скрипта / функции, Component генерирует промежуточный DOM, который заменяет настоящий HTML DOM. Затем делается дополнительный шаг для перевода этого промежуточного DOM в реальный HTML DOM.

Как часть промежуточного поколения DOM, Component также присоединяет обработчики событий и связывает данные, содержащиеся в props и state .

Если идея промежуточного DOM звучит немного чуждо, не пугайтесь. Вы уже видели эту стратегию, принятую языковыми средами исполнения (или виртуальными машинами) для интерпретируемых языков. Наша собственная среда выполнения JavaScript сначала генерирует промежуточное представление перед тем, как выплевывать собственный код. Это также верно для других языков на основе виртуальных машин, таких как Java, C #, Ruby, Python и т. Д.

React умно применяет эту стратегию для создания промежуточного DOM перед созданием окончательного HTML DOM. Промежуточный DOM — это просто граф объектов JavaScript, который не отображается напрямую. Существует шаг перевода, который создает настоящий DOM. Это базовый метод, который заставляет React выполнять быстрые манипуляции с DOM.

Чтобы получить более полное представление о том, как React заставляет все это работать, давайте углубимся немного глубже; начиная с Component . Компонент является основным строительным блоком в React. Вы можете составить пользовательский интерфейс вашего приложения, собрав дерево компонентов. Каждый Компонент обеспечивает реализацию для метода render() , где он создает промежуточный DOM. Вызов React.renderComponent() для корневого компонента приводит к рекурсивному React.renderComponent() вниз по дереву компонентов и созданию промежуточного DOM. Промежуточный DOM затем преобразуется в настоящий HTML DOM.

Компонент-дом-дерево

Поскольку создание промежуточного DOM является неотъемлемой частью Компонента, React предоставляет удобное расширение на основе XML для JavaScript, называемое JSX, для построения дерева компонентов в виде набора узлов XML. Это облегчает визуализацию и анализ DOM. JSX также упрощает сопоставление обработчиков событий и свойств как атрибутов xml. Поскольку JSX является языком расширения, есть инструмент (командная строка и в браузере) для генерации окончательного JavaScript. Узел JSX XML отображается непосредственно на Компонент. Стоит отметить, что React работает независимо от JSX, а язык JSX упрощает создание промежуточного DOM.

Базовую платформу React можно скачать с их сайта . Кроме того, для преобразования JSX → JS вы можете использовать встроенный в браузер JSXTransformer или использовать инструмент командной строки, называемый реагирующий инструмент (устанавливается через NPM). Вам понадобится установить Node.js, чтобы скачать его. Инструмент командной строки позволяет предварительно скомпилировать файлы JSX и избежать перевода в браузере. Это определенно рекомендуется, если ваши файлы JSX большие или их много.

Хорошо, мы до сих пор видели много теории, и я уверен, что вам не терпится увидеть какой-то реальный код. Давайте погрузимся в наш первый пример:

Несмотря на простоту, приведенный выше код действительно покрывает значительную часть площади React:

  • Мы создаем простой компонент, используя React.createClass и передавая объект, который реализует некоторые основные функции. Наиболее важным из них является render() , который генерирует промежуточный DOM.
  • Здесь мы используем JSX для определения DOM, а также присоединяем обработчик событий mousedown. Синтаксис {} полезен для включения выражений JavaScript для атрибутов ( onMouseDown={this.handleClick} ) и дочерних узлов ( <span class="count">{this.state.count}</span> ). Обработчики событий, связанные с использованием синтаксиса {}, автоматически привязываются к экземпляру компонента. Таким образом, this внутри функции обработчика событий относится к экземпляру компонента. Комментарий в первой строке /** @jsx React.DOM */ является подсказкой для преобразователя JSX выполнить перевод в JS. Без этой строки комментариев перевод не будет выполняться .

Мы можем запустить инструмент командной строки ( jsx ) в режиме наблюдения и автоматически скомпилировать изменения из JSX → JS. Исходные файлы находятся в папке / src, а выходные данные генерируются в / build .

1
jsx —watch src/ build/

Вот сгенерированный файл JS:

Обратите внимание, как теги <div/> и <span/> отображаются на экземпляры React.DOM.div и React.DOM.span .

  • Теперь вернемся к нашему примеру кода. Внутри handleMouseDown мы используем this.props для чтения переданного свойства сообщения . Мы устанавливаем сообщение в последней строке фрагмента при вызове React.renderComponent() где мы создаем компонент <Simple/> , Цель this.props — сохранить данные, которые были переданы компоненту. Он считается неизменным, и только компоненту более высокого уровня разрешено вносить изменения и передавать его в дерево компонентов.
  • Внутри handleMouseDown мы также устанавливаем некоторое пользовательское состояние с помощью this.setState() для отслеживания того, сколько раз сообщение отображалось. Вы заметите, что мы используем this.state в this.state render() . setState() вы вызываете setState() , React также запускает метод render() для синхронизации DOM. Помимо React.renderComponent() , setState() — это еще один способ принудительного визуального обновления.

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

Комбинация промежуточных DOM и синтетических событий дает вам стандартный и согласованный способ определения интерфейсов для разных браузеров и даже устройств.

Компоненты в платформе React имеют определенный жизненный цикл и представляют собой конечный автомат с тремя различными состояниями.

Компонент-жизненный цикл

Компонент оживает после того, как установлен . Монтирование приводит к прохождению рендеринга, который генерирует дерево компонентов (промежуточный-DOM). Это дерево преобразуется и помещается в контейнер-узел реального DOM. Это прямой результат вызова React.renderComponent() .

После подключения компонент остается в состоянии обновления . Компонент обновляется, когда вы меняете состояние с помощью setState() или меняете реквизиты с помощью setProps() . Это, в свою очередь, приводит к вызову render() , который синхронизирует DOM с данными ( props + state ). Между последующими обновлениями React будет вычислять дельту между предыдущим компонентным деревом и вновь созданным деревом. Это высоко оптимизированный шаг (и флагманская функция), который сводит к минимуму манипуляции на реальном DOM.

Конечное состояние Unmounts . Это происходит, когда вы явно вызываете React.unmountAndReleaseReactRootNode() или автоматически, если компонент был дочерним, который больше не генерировался в вызове render() . Чаще всего вам не приходится иметь дело с этим и просто позволить React делать правильные вещи.

Теперь было бы большой упущением, если бы React не сказал вам, когда он перемещался между состояниями Mounting-Update-Unmounting . К счастью, это не так, и есть хуки, которые вы можете переопределить, чтобы получать уведомления об изменениях жизненного цикла. Имена говорят сами за себя:

  • getInitialState() : подготовить начальное состояние компонента
  • componentWillMount()
  • componentDidMount()
  • componentWillReceiveProps()
  • shouldComponentUpdate() : полезно, если вы хотите контролировать, когда рендеринг должен быть пропущен.
  • componentWillUpdate()
  • render()
  • componentDidUpdate()
  • componentWillUnmount()

Методы componentWill* вызываются до изменения состояния, а методы componentDid* — после.

Некоторые из названий методов, похоже, были взяты из фреймворков Cocoa в Mac и iOS

Внутри дерева компонентов данные всегда должны течь вниз. Родительский компонент должен установить props дочернего компонента для передачи любых данных от родителя дочернему компоненту. Это называется парой владелец-владелец . С другой стороны, пользовательские события (мышь, клавиатура, прикосновения) всегда будут пузыриться от дочернего элемента вплоть до корневого компонента, если только не обработаны между ними.

данных событий потока

Когда вы создаете промежуточный DOM в render() , вы также можете назначить свойство ref для дочернего компонента. Затем вы можете обратиться к нему от родителя, используя свойство refs . Это изображено во фрагменте ниже.

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

React также поддерживает концепцию Mixin для извлечения многократно используемых частей поведения, которые можно вводить в разрозненные компоненты. Вы можете передать mixins, используя свойство mixins компонента.

Теперь давайте реализуем и создадим более полный компонент, который использует эти функции.

В этом примере мы создадим редактор, который принимает простой DSL (предметно-ориентированный язык) для создания фигур. По мере ввода вы увидите соответствующий вывод на боковой панели, что даст вам обратную связь в реальном времени.

DSL позволяет создавать три вида фигур: эллипс, прямоугольник и текст. Каждая фигура указана в отдельной строке вместе с набором стилевых свойств. Синтаксис прост и немного заимствует из CSS. Для разбора строки мы используем регулярное выражение, которое выглядит следующим образом:

Например, следующий набор строк описывает два прямоугольника и текстовую метку …

… генерация вывода, показанного ниже:

реагирует-форму

Хорошо, давайте продолжим и создадим этот редактор. Мы начнем с HTML-файла ( index.html ), в который мы помещаем разметку верхнего уровня и включаем библиотеки и скрипты приложения. Я только показываю соответствующие части здесь:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<body>
<select class=»shapes-picker»>
  <option value=»—«>— Select a sample —</option>
  <option value=»react»>React</option>
  <option value=»robot»>Robot</option>
</select>
<div class=»container»></div>
 
<!— Libraries —>
<script src=»../../lib/jquery-2.0.3.min.js»></script>
<script src=»../../lib/react.js»></script>
 
 
<!— Application Scripts —>
<script src=»../../build/shape-editor/ShapePropertyMixin.js»></script>
<script src=»../../build/shape-editor/shapes/Ellipse.js»></script>
<script src=»../../build/shape-editor/shapes/Rectangle.js»></script>
<script src=»../../build/shape-editor/shapes/Text.js»></script>
 
<script src=»../../build/shape-editor/ShapeParser.js»></script>
<script src=»../../build/shape-editor/ShapeCanvas.js»></script>
<script src=»../../build/shape-editor/ShapeEditor.js»></script>
 
<script src=»../../build/shape-editor/shapes.js»></script>
<script src=»../../build/shape-editor/app.js»></script>
</body>

В приведенном выше фрагменте container div содержит нашу DOM, сгенерированную React. Наши прикладные скрипты включены в каталог /build . Мы используем JSX в наших компонентах и jsx командной строки ( jsx ) помещает преобразованные файлы JS в /build . Обратите внимание, что эта команда-наблюдатель является частью модуля NPM react-tools .

1
jsx —watch src/ build/

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

  • ShapeEditor : корневой компонент в дереве компонентов
  • ShapeCanvas : отвечает за генерацию компонентов формы (эллипс, прямоугольник, текст). Он содержится в ShapeEditor.
  • ShapeParser : отвечает за анализ текста и извлечение списка определений форм. Он анализирует строку за строкой с регулярным выражением, которое мы видели ранее. Неверные строки игнорируются. На самом деле это не компонент, а вспомогательный объект JS, используемый ShapeEditor.
  • Эллипс, Прямоугольник, Текст : Компоненты формы. Они становятся детьми ShapeCanvas.
  • ShapePropertyMixin : предоставляет вспомогательные функции для извлечения стилей, найденных в определениях форм. Это смешано в три компонента shape с использованием свойства mixins .
  • приложение : точка входа для редактора. Он генерирует корневой компонент (ShapeEditor) и позволяет вам выбрать образец формы из выпадающего списка.

Взаимосвязь этих сущностей показана в аннотированном дереве компонентов:

компонент дерева

Давайте рассмотрим реализацию некоторых из этих компонентов, начиная с ShapeEditor.

Как следует из названия, ShapeEditor предоставляет возможность редактирования, генерируя <textarea/> и живую обратную связь на <ShapeCanvas/< . Он прослушивает событие onChange (события в React всегда onChange с использованием onChange верблюда) в <textarea/> и при каждом изменении устанавливает свойство text состояния компонента. Как упоминалось ранее, всякий раз, когда вы устанавливаете состояние с помощью setState() , render вызывается автоматически В этом случае render() ShapeEditor вызывается, когда мы анализируем текст из состояния и перестраиваем фигуры. Обратите внимание, что мы начинаем с начального состояния пустого текста, которое устанавливается в getInitialState() .

Для разбора текста на набор фигур мы используем экземпляр ShapeParser . Я оставил детали парсера, чтобы обсуждение было сосредоточено на React. Экземпляр анализатора создается в хуке componentWillMount() . Это вызывается непосредственно перед монтированием компонента и является хорошим местом для любых инициализаций до того, как произойдет первый рендеринг.

Как правило, рекомендуется выполнять всю сложную обработку с помощью метода render() . Обработчики событий просто устанавливают состояние, в то время как render() является центром всей вашей базовой логики.

ShapeEditor использует эту идею, чтобы выполнить синтаксический анализ внутри функции render() и перенаправить обнаруженные фигуры, установив свойство ShapeCanvas объекта ShapeCanvas . Вот как данные перетекают в дерево компонентов от владельца ( ShapeEditor ) к собственному ( ShapeCanvas ).

Последнее, на что следует обратить внимание, это то, что у нас есть комментарий в первой строке, обозначающий перевод JSX → JS.

Далее мы перейдем к компонентам ShapeCanvas и Ellipse, Rectangle и Text.

p> ShapeCanvas довольно прост с его основной ответственностью генерации соответствующих компонентов <Ellipse/> , <Rectangle/> и <Text/> из переданных определений формы ( this.props.shapes ). Для каждой фигуры мы передаем проанализированные свойства с выражением атрибута: properties={shape.properties} .

Еще одно отличие состоит в том, что наше дерево компонентов не является статичным, как в ShapeEditor. Вместо этого он динамически генерируется зацикливанием передаваемых фигур. Мы также показываем сообщение "No Shapes Found" если нечего показывать.

Все формы имеют одинаковую структуру и отличаются только стилем. Они также используют ShapePropertyMixin для обработки генерации стиля.

Вот Эллипс:

Реализация для extractStyle() предоставляется ShapePropertyMixin .

Компонент Rectangle следует его примеру, конечно, без стиля border-radius . Компонент Text имеет дополнительное свойство под названием value которое устанавливает внутренний текст для <div/> .

Вот текст, чтобы прояснить это:

app.js — это место, где мы собираем все вместе. Здесь мы визуализируем корневой компонент ShapeEditor а также предоставляем поддержку для переключения между несколькими образцами фигур. Когда вы выбираете другой образец из выпадающего ShapeEditor , мы загружаем некоторый предопределенный текст в ShapeEditor и ShapeCanvas обновление ShapeCanvas . Это происходит в readShapes() .

Чтобы проявить творческую сторону, вот робот, созданный с использованием редактора форм:

робот

Уф! Это была довольно длинная статья, и, достигнув этой точки, вы должны иметь чувство достижения!

Здесь мы исследовали множество концепций: неотъемлемую роль компонентов в платформе, использование JSX для простого описания дерева компонентов (промежуточного DOM), различные хуки для подключения к жизненному циклу компонентов, использование state и props для управления процесс рендеринга, использование Mixins, чтобы выделить многократно используемое поведение и, наконец, собрать все это вместе с примером Shape Editor.

Я надеюсь, что эта статья даст вам достаточную поддержку для создания нескольких приложений React для себя. Чтобы продолжить исследование, вот несколько полезных ссылок: