7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. Web Development

Введение в React фреймворк

Read Time: 15 mins

Russian (Pусский) translation by Masha Kolesnikova (you can also view the original English article)

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

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

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

Мотивы

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

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

Обзор высокоуровневой структуры приложений JS

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

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

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

mvc-flowmvc-flowmvc-flow

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

Что отличает React?

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

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

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

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

React в глубине

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

component-dom-treecomponent-dom-treecomponent-dom-tree

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

Tooling

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

Простой компонент

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

Хотя этот код довольно прост, приведенный выше код действительно покрывает хорошее количество площади поверхности React:

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

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

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

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

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

Синтетические события

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

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

Жизненный цикл компонентов

Компоненты в структуре React имеют определенный жизненный цикл и воплощают машину состояний, состоящую из трех разных состояний.

component-lifecyclecomponent-lifecyclecomponent-lifecycle

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

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

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

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

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

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

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

Различные фичи

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

data-event-flowdata-event-flowdata-event-flow

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

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

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

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

Редактор формы, построенный с использованием React

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

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

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

... генерируя вывод, показанный ниже:

react-shapes

Настройка

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

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

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

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

Связь этих объектов показана в аннотированном дереве компонентов:

component-treecomponent-treecomponent-tree

Компонент ShapeEditor

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

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

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

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

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

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

ShapeCanvas для создания фигур

Затем мы перейдем к компонентам 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

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

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

robot

И это для вас!

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

Мы рассмотрели здесь много концепций: интегральная роль компонентов во фреймворке, использование JSX для легкого описания дерева компонентов (aka intermediate-DOM), различные хуки для подключения к lifecyle компонента, использование state и props для управления процессом визуализации, использование Mixins для исключения повторного использования и, наконец, вытягивания всего этого вместе с примером редактора Shape.

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

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.