Pull to refresh

Построение надежных веб-приложений на React: Часть 1, браузерные прототипы

Website development *JavaScript *ReactJS *
Translation
Tutorial
Original author: Matt Hinchliffe
Перевод статьи «Building robust web apps with React: Part 1, in-browser prototypes», Matt Hinchliffe

От переводчика: это первая статья из цикла «Building robust web apps with React».
Переводы:


Когда я смотрю на то, как устроены браузеры и протоколы, на которых работает веб, мне становится как-то тревожно. Есть столько всего, что может и, обычно, идет не так, что становится удивительно, как хоть что-то из того, что мы пишем, вообще работает. Надежность «вопреки всему» происходит от отказоустойчивости и обратной совместимости, которые укоренились в ключевые части веб-стека. Браузер всегда сделает все возможное, чтобы отобразить, что-то полезное, будь-то парсинг плохо написанного документа, в котором невозможно получить зависимости, или, который на 10 лет устарел.

image
404PageFound содержит до сих пор работающие сайты, созданные еще в 1993 году.

Мне кажется, что это фундаментальное свойство веба забывается. Мы, как разработчики, стремимся все больше и больше соответствовать ожиданиям поставленными другими платформами. Мы особенно завидуем рабочему окружению мобильных разработчиков, мы тоже хотим строить динамические и гибкие приложения. Есть множество удивительно умных людей, которые, прямо сейчас, создают приложения, которые позволяют нам делать это, сейчас и вправду замечательное время для работы в этой индустрии. Однако, я не думаю, что текущий набор инструментов; Angular, Ember, Knockout и др. это, то в чем заключается будущее браузерной разработки, потому как они не являются надежными по своей сути.

Я обеспокоен тем, что вижу сайты, которые делают JavaScript краеугольным камнем работы с содержимым на данный момент и на будущее. Это построение основы на самой хрупкой части стека.
— Jeremy Keith, Time (Full Frontal 2013)

Дополнительная сложность и нагрузка, которую мы перемещаем на браузер, означает, что пользователь может ничего не увидеть, даже если произойдет минимальная проблема. Это проблема не столько вышеупомянутых инструментов, сколько в современном подходе построения веб-сайтов; пустой тег body у страницы просто недопустим. Наши сайты должны быть хотя бы доступны, когда они запрошены при помощи дрянного соединения или запущены в старом браузере. Мы должны осознать факт, что веб-браузер совсем не рабочая среда смартфона, так как здесь столько всего, что мы не можем контролировать!

image
Главная страница Squarespace прячет все содержимое полагаясь на то, что JavaScript обработает его видимость.

Я здесь не для того, чтобы проповедовать философию Лудитов, я, правда, не хочу сказать, что мы должны разрабатывать страницы на чистом HTML. Мы, определенно, можем побороть хрупкость, которую мы вводим на наши сайты, но чтобы сделать это, мы должны переосмыслить некоторое техники, которые мы используем, и для есть несколько интересных инструментов, мой любимый из них, это библиотека React от разработчиков Facebook и Instagram.

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

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

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

React позволяет приложениям быть написанными выразительно–интерактивными по умолчанию, но также возвращает надежность в динамические веб-сайты. Это разумно и делает только небольшой, если вообще делает, штраф в производительности.

Пример приложения


Пример, который я собираюсь создать, это табло отправки для станций Лондонского метро при помощи TrackerNet API или “Tube Tracker”, для краткости.

image

Данное приложение имеет только четыре состояния: отображение приветствия, загрузка или сообщение об ошибке и отображение отправки поездов со станции. Для сертифицированного разработчика производственного уровня, приложение может быть представлено следующей диаграммой:

image

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

TrackerNet API, это продукт прошлой эпохи, он плохо документирован и в нем недостает данных о станциях и целых ветках. Проблемы с TrackerNet были хорошо описаны Крисом Эпплгейтом (Chris Applegate) и решены в его опенсорс проекте When’s My Transport. К счастью, кажется, что его ветхий API будет вскоре заменен.

Быстрое браузерное прототипирование


React приложения могут быть быстро спрототипированы в браузере без какой либо подготовки. Я предпочитаю писать мои компоненты с использованием опционального синтаксиса JSX, который позволят им быть написанными очень похожими на HTML синтаксис. Если вам кажется, что встраивание HTML в JavaScript неправильно, это можно понять, учитывая годы их разделения, но для меня это оказалось более продуктивным подходом, и это делает визуализацию проще, чем при использовании чистого JavaScript. Подключение React и JSX трансформатора (доступных на Facebook CDN), это все что вам нужно, чтобы начать:
<html>
  <head>
    <title>My React App</title>
    <script src="http://fb.me/react-0.9.0.js"></script>
    <script src="http://fb.me/JSXTransformer-0.9.0.js"></script>
  </head>
  <body>
    <script type="text/jsx">
      /** @jsx React.DOM */
    </script>
  </body>
</html>

Установка атрибута type элемента script в что-нибудь другое чем text/javascript позволяет не-JavaScript данным встраиваться на страницу. Поскольку элемент script не предназначен для пользователя, он не имеет визуального отображения на странице.

Иерархия компонентов


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

image

Наш каркас может быть организован в следующую иерархию:
  • TubeTracker содержит приложение
    • Network отображает каждую линию в сети
      • Line отображает станции на линии

    • Predictions контролирует состояние доски отправки
      • DepartureBoard отображает текущую станцию и платформы


Свойства и состояние


React имеет два типа данных; “свойства” передаваемые между компонентами и “состояние”, которое хранится внутри компонента. Компонент может изменять состояние, но его свойства не изменяются, что хорошо, так как, в идеале, должен быть только один источник правды. Ключевое архитектурное решение при проектировании React приложения, это решение о том, какие данные требуются каждым компонентом, и где должен быть это источник.

image

Tube Tracker требует только три вида данных: данные сети (линии, станции и тд.), выбранная пользователем линия и станция, и данные расписания получаемые из TrackerNet.

Данные сети, потребляются только компонентами Network и Line чтобы предоставлять списки станций, а также компонентом TubeTracker для валидации пользовательского ввода. Это большой объем данных, поэтому лучше обрабатывать их внешне и затем передавать в приложение.

Выбранная пользователем линия и станция используется компонентом Predictions, но выбранные условия предосталяются компонентом Line. Следуя иерархии компонентов, их общий предок, это TubeTracker, так что выбранная линия и станция должны хранится в состоянии компонента TubeTracker.

Наконец, данные прогнозов TrackerNet используются компонентами Trains, DepartureBoard и Predictions, но не компонентом TubeTracker, так что они должны храниться в компоненте Predictions.

Но есть проблема, данные должны быть переданны обратно вверх по иерархии. Когда ввод пользователя полученный компонентом Line, его предок, TubeTracker должен знать об этом.

Мышление в React(Thinking in React) — более детальный обзор иерархии компонентов и различий между свойствами и состоянием, это точно стоит прочесть.

Коммуникация компонентов


Так как данные могут передаваться только вниз по иерархии, мы должны использовать другую технику для передачи данных наверх. Есть два основных способа, чтобы сделать это.

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

var Parent = React.createClass({
  handleClick: function(e) {...},
  render: function() {
    return <Child callback={this.handleClick} />;
  }
});

var Child = React.createClass({
  render: function() {
    return <a onClick={this.props.callback}>Click me</a>;
  }
});

Для более дальних уведомлений система публикация/подписка (publish/subscribe) будет более гибкой и простой для поддержки. Это может быть сделано нативными JavaScript событиями или при помощи библиотеки PubSubJS, путем привязки к методам жизненного цикла компонента.

var Parent = React.createClass({
  handleMyEvent: function(e) {...},
  componentWillMount: function() {
    window.addEventListener("my-event", this.handleMyEvent, false);
  },
  componentWillUnmount: function() {
    window.removeEventListener("my-event", this.handleMyEvent, false);
  },
  render: function() {...}
});

var Grandchild = React.createClass({
  handleClick: function(e) {
    var customEvent = new CustomEvent("my-event",  {
      detail: { ... },
      bubbles: true
    });
    this.refs.link.getDOMNode().dispatchEvent(customEvent);
  },
  render: function() {
    return <a ref="link" onClick={this.handleClick}>Click me</a>;
  }
});

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


У компонентов есть краткий API, чтобы зацепиться за их жизненный цикл; создание (mounting), обновление и уничтожение (unmounting). В отличии от других подходов построения динамических интерфейсов, эта функциональность встроена в определение компонента. В примере коммуникации компонентов я использовал методы componentWillMount и componentWillUnmount для добавления и удаления обработчиков событий, но есть и другие методы, которые дают детальный контроль над свойствами и состоянием компонентов. В приложении Tube Tracker я также использовал следующие методы:
  • componentDidMount вызывается после того, как компонент был отрисован, он полезен как точка интеграции с другим кодом, который зависит от сгенерированного DOM.
  • componentWillReceiveProps вызывается каждый раз, когда компонент получает новые свойства, это полезно для отмены запущенных на данный момент операций, которые будут задеты добавленными свойствами.
  • shouldComponentUpdate полезный хук, для ручного контроля того, нуждается ли в перерисовке изменение состояния или свойств.


Вот что я сделал раньше


Итак, мы установили браузерное окружение, разбили наш UI на компоненты, выяснили, какие данные нам необходимы, где должны они храниться и как они распределны в приложении, пришло время для демонстрации!

image

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

Во второй части я опишу оптимизацию приложения; установку инструментов по оптимизации кода для браузера. Пожалуйста, комментируйте или твитните мне, я буду рад получить отзывы.
Tags:
Hubs:
Total votes 30: ↑28 and ↓2 +26
Views 42K
Comments Comments 8