Статьи

Веб-приложения на основе компонентов с React

ReactJS — это инструментарий для создания веб-приложений на основе компонентов. React демонстрирует быструю и легкую работу благодаря умному использованию DOM-моделирования, чтобы минимизировать количество выполняемых DOM-манипуляций и поисков. Компоненты React написаны в смеси JavaScript и XML, но скомпилированы в чистый Javascript с использованием инструментов компилятора React. Вот пример компонента, который отображает текущее время и выдает предупреждение, когда вы нажимаете на него:

<script type="text/jsx"> /** @jsx React.DOM */ var Clock = React.createClass({ render: function() { return ( <div onClick={this.sayHi}> The time is: {this.props.when.toLocaleString()} </div> ); }, sayHi: function() { alert("Hello"); } }); React.renderComponent( <Clock when={new Date()} />, document.body ); </script> 

Поскольку код сочетает в себе XML и JavaScript, синтаксис не может быть напрямую выполнен браузером. Вот почему тегу script требуется тип text/jsx . Чтобы запустить его, код должен быть скомпилирован в чистый JavaScript с помощью инструментов компилятора React. Кроме того, веб-сайт может включать в себя компилятор реального времени JSX (еще одну библиотеку JavaScript), хотя обычно он зарезервирован только для целей разработки или тестирования.

Компилятор React также требует, чтобы в верхней части была размещена строка комментария с надписью @jsx React.DOM . Эта строка сообщает компилятору React, что встроенный код должен быть скомпилирован с параметром React.DOM . Это может измениться в будущем, но верно для React v0.10 RC.

После того, как вы скомпилируете код, XML будет преобразован, и будет сгенерирован следующий код JavaScript. Как вы можете видеть, XML является просто синтаксическим сахаром, который позволяет интерфейсу быть написанным в синтаксисе HTML.

 <script type="text/javascript"> var Clock = React.createClass({ render: function() { return React.DOM.div( {onClick: this.sayHi}, "The time is: ", this.props.when.toLocaleString() ); }, sayHi: function() { alert("Hello"); } }); React.renderComponent( Clock({when: new Date()}), document.body ); </script> 

Использование XML

Каждый компонент React — это просто объект с функцией render() . Эта функция возвращает XML, который описывает внешний вид интерфейса. Важно помнить, что XML не является прямым отображением HTML, к которому мы привыкли. Когда вы пишете <table><tr><td></td></tr></table> , вы не создаете таблицу. В действительности вы создаете три компонента ( table , tr и td ) и передаете один компонент в качестве параметра другому.

Это также означает, что не каждый атрибут, заданный вами в XML, будет отображаться в результирующем HTML. Компонент должен специально обрабатывать этот атрибут для его использования. К счастью, стандартный набор компонентов React поддерживает все распространенные атрибуты, такие как id , href , src , type , checked и т. Д.

Одно отклонение от нормы заключается в том, что все атрибуты должны быть в верблюжьих. Например, <input onclick="" /> записывается как <input onClick="" /> , а <td colspan="3"> становится <td colSpan="3"> . Кроме того, атрибуту style уделяется особое внимание, так как он ожидает хеш-объект стилей в качестве параметра вместо обычного синтаксиса CSS. Примерным атрибутом style будет <div style={ {fontFamily:"Arial", marginLeft:10} }></div> .

Другая особенность XML заключается в том, что его синтаксис более строг, чем HTML. Все теги XML должны сопровождаться закрывающим тегом ( </td> , </p> ) или быть самозакрывающимися ( <input /> ).

Поскольку XML просто используется как метод для вызова компонентов, все пользовательские компоненты вызываются одинаково.

 <!-- inside the render function --> <table> <tr> <td> <ShoppingCart size="mini"> <List /> </ShoppingCart> </td> </tr> </table> 

Имя компонента — это просто имя переменной, которое вы присвоили ему при создании:

 var ShoppingCart = React.createClass({ /* ... */ }); 

Возможно, вы заметили фигурные скобки внутри XML. Они содержат выражения JavaScript, которые будут скопированы дословно при компиляции кода в JavaScript.

Роль компонентов

Наиболее важным аспектом компонента является функция render() . Эта функция не отображает HTML и не генерирует DOM-узел для добавления на веб-страницу. Его роль заключается в создании дерева объектов JavaScript, которые напоминают, как должен выглядеть DOM; своего рода «симуляция DOM», если хотите. Но все это делается с помощью JavaScript-объектов, которые являются легкими и легко собираемыми.

Имитация DOM не может быть использована напрямую. Вместо этого он передается в React, который использует разностный алгоритм для обнаружения изменений из последнего смоделированного DOM. Эти различия затем применяются как последовательность операций обновления к реальной веб-странице.

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

DOM Simulation

Поскольку React полагается на разностный алгоритм для обнаружения изменений и обновлений, больше нет необходимости писать код для изменения DOM. Это означает, что вам больше не нужно вызывать setAttribute() или input.value . Все это незаметно обрабатывается движком React.

Все, что нужно сделать вашему компоненту — это render() функцию render() которая создает симулированный DOM. Каждый раз, когда необходимо обновить страницу, вызывается render() , и генерируется новый имитированный DOM. Это означает меньше кода для написания и поддержки.

Причина, по которой это возможно, заключается в том, что моделируемый DOM выполняется быстро, что позволяет React свести к минимуму снижение производительности при необходимости регенерировать все дерево при каждом рендеринге. React также может использовать несколько эвристик, чтобы приблизить задачу обхода дерева O (n ^ 3) к задаче O (n).

Обработка событий

Обработчики событий присоединяются к компонентам с помощью таких атрибутов, как onClick onMouseOver , onKeyPress и т. Д. Эти обработчики событий работают только с тегами HTML, а не с пользовательскими компонентами. Для пользовательских компонентов вы должны передать атрибут одному из тегов HTML в пользовательском компоненте. Пример, содержащий обработчики событий, показан ниже.

 <!-- inside the render function --> <div> <button onClick={this.actionA} /> <button onClick={this.actionB} /> </div> 

За кулисами React присоединяет прослушиватель событий к базовому узлу и использует делегирование событий для распространения события на целевой компонент. Это сделано для повышения производительности, поэтому вы можете безопасно добавлять события в каждую строку списка.

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

 var MainApp = React.createClass({ render: function() { return ( <div> <ShoppingCart onCheckout={this.checkoutCart} onEmpty={this.emptyCart} /> </div> ); }, checkoutCart: function() { /* ... */ }, emptyCart: function() { /* ... */ } }); 

Атрибуты

Данные передаются компонентам с использованием атрибутов:

 var myData = {list: [], amount: 0, taxes:1.15}; var MainApp = React.createClass({ render: function() { return <ShoppingCart goods={myData} />; } }); var ShoppingCart = React.createClass({ render: function() { return <div>Amount: {this.props.goods.amount}</div>; } }); 

Затем компонент извлекает данные, обращаясь к ним из свойства this.props . В отличие от традиционного HTML, где значения атрибутов являются строками, атрибутам React можно назначать сложные объекты, поскольку после компиляции кода все они преобразуются в объекты JavaScript.

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

Большие компоненты могут хранить данные внутри в форме state . Состояния могут рассматриваться как хранилища данных, которые являются частными для компонента. Данные в состояниях устанавливаются путем вызова setState(objectHash) для компонента. Это делает данные доступными из свойства this.state . Вызов setState() запускает обновление компонента, которое вызывает render() . Использование state и props аналогично, но семантически отличается для удобства разработчиков компонентов.

Надежность атрибутов

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

Делая это масштабируемым

До сих пор мы видели, что React может обновлять интерфейс так часто, как он этого хочет, даже для незначительных изменений данных, поскольку React рассчитает минимальный набор изменений, необходимых для обновления DOM, и, следовательно, будет достаточно эффективен в этом. Однако по пути вы можете столкнуться с проблемами производительности или просто захотите оптимизировать свои компоненты. Секрет оптимизации находится в функции shouldComponentUpdate() которая вызывается перед render() . Каждый компонент имеет эту функцию, которая может переопределять, обновляется ли конкретный компонент и его дочерние элементы. Эта функция принимает новый props и state качестве аргументов. Вы можете использовать их, чтобы проверить, действительно ли обновление необходимо.

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

Для более надуманного примера, все приложение может быть создано на основе сверки старых значений с новыми значениями. Обычно, когда новые данные доступны с сервера, библиотека модели данных, такая как Backbone, должна определить, какое конкретное свойство было изменено, и должны быть запущены соответствующие обработчики для этого свойства. При таком подходе, когда доступны новые данные, они немедленно заменяют старые данные, и интерфейс обновляется. Поскольку обновление распространяется по дереву, каждому компоненту нужно только проверить, отличаются ли новые данные от старых данных в shouldComponentUpdate() , чтобы определить, нужно ли обновить эту ветвь дерева.

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

Вывод

Поскольку React поставляется только с инструментами для создания пользовательского интерфейса, у него нет инструментов для структурирования ваших данных или структурирования ваших поставщиков услуг. Люди успешно используют React в качестве интерфейса пользовательского интерфейса и используют Angular в качестве фреймворка. Другие могут заставить его работать, используя React и ничего больше. Нет рецепта, какой метод лучше. Работает множество комбинаций, что говорит о гибкости, которую может предоставить React. Вам просто нужно найти свою собственную комбинацию. Посетите страницу проекта и дайте нам знать, как работает React для вас.