Побудова надійних веб-додатків на React: Частина 4, серверна генерація

      Переклад статті «Building robust web apps with React: Part 4, server-side rendering», Matt Hinchliffe
 
Від перекладача: це переклад четвертої частини циклу статей «Building robust web apps with React»
Переклади:
 Кілька місяців тому я написав першу частину цієї серії , я дуже зацікавився можливостями використання React'а для створення розумних програм, які можуть уникнути слабкостей, існуючих в багатьох сучасних JavaScript додатках. Нарешті, я збираюся зробити так, щоб моє додаток запускалось спочатку на сервері, а потім запускало той же код в браузері, завершуючи цикл изоморфного або адаптивно-гібридного додатки.
 
У попередній частині цієї серії я блукав у пошуках стратегії тестування, і ця частина не сильно відрізняється. І хоча тут є приклади і базові демо изоморфного JavaScript, тут не так багато опенсорс реалізацій для вивчення. У бік деталі реалізації, для мене, найбільша відмінність, від написання клієнтського JavaScript, це те, що e мене спочатку є дані.
 
 

Реорганізація потоку даних

Браузерна версія мого програми Tube Tracker має дуже просту модель потоку даних, але дана модель не зовсім підходить для серверної сторони. У браузері дані запитуються AJAX запитом, як тільки завантажиться додаток, але изоморфное додаток повинен надавати в браузер готові HTML сторінки.
 
React додаток має одну точку доступу до стека компонентів — корінний компонент отрісовивается методом
renderComponent
або
renderComponentToString
, таким чином дані повинні надходити в вершину стека і передаватися вниз. Це йде врозріз з звичайним мисленням при роботі з React, так як дані повинні оброблятися тільки тим компонентом, якому вони потрібні.
 
 image
 
Так як дані надходять вниз по стеку, компоненти, які спочатку повинні були запитувати дані, тепер можуть використовувати метод
getInitialState
, як передзавантажувальне дані. В іншому компоненти не змінюються, крім того, що пропускається стадія початкового завантаження стану.
 
 
// ~/app/component/predictions.jsx
var Predictions = React.createClass({
  getInitialState: function() {
    return {
      status: this.props.initialData ? "success" : "welcome",
      predictionData: this.props.initialData
    };
  },
  ...
});

Дані надані в корінь додатки на серверній стороні, також повинні бути отримані на клієнтській стороні , якщо додаток повинен завантажуватися без тих же даних наданих на клієнтській стороні, тоді воно буде перерендерено свого початкова, пусте стан. Найпростіший спосіб надати початкові дані, це згенерувати їх в script елемент, а потім підібрати їх при ініціалізації програми:
 
 
// ~/app/browser/bootstrap.jsx
var React = require("react");
var networkData = require("../common/data");
var TubeTracker = require("../component/tube-tracker.jsx");

window.app = (function() {
  ...
  var initialData = JSON.parse(document.getElementById("initial-data").innerHTML);
  return React.renderComponent(<TubeTracker networkData={networkData} initialData={initialData} />, document.body);
})();

 

React на сервері

Компоненти, що використовують звичайний JSX синтаксис, перед використанням мають бути трансформовані в звичайний JavaScript, але на сервері це не обов'язково означає крок прекомпіляції. Бібліотека React Tools може виробляти трансформацію «на льоту». І вона упакована в пакет Node-JSX , який прозоро інтерпретує модулі, як тільки вони будуть потрібні. Node-JSX потребує тільки в одноразовому підключенні до додатка, так як метод
require
працює глобально, але користуйтеся цим обережно: парсер використовуваний бібліотекою React Tools може впиратися на деяких особливо креативних моментах використання JavaScript, так що, в цілях безпеки і продуктивності, краще виносити компоненти у файли з розширенням
.jsx
.
 
 
// ~/server.jsx
require("node-jsx").install({ extension: ".jsx" });

var config = require("./config");
var express = require("express");
var API = require("./app/server/api");
var Bootstrap = require("./app/server/bootstrap.jsx");

Стек компонентів генерується так само як в браузері, але замість створення динамічного древа, потрібна тільки HTML рядок. Для цих цілей React надає метод
renderComponentToString
, він просто запускає методи
getInitialState
і
componentWillMount
для кожного компонента. Компоненти повинні транслюватися на сервер, не викликаючи проблем, якщо тільки ніде не використовується браузерні (browser-specific) код, так що переконаєтеся, щоб весь клієнтський код був переміщений в метод
componentDidMount
.
 
 

Остальное на сервері

Останні кілька кроків для надання початкового HTML коду в браузер, полягають в конкретній реалізації, але для довідки я швидко пройдуся по закулісної реалізації додатки Tube Tracker.
 
Додаток вже використовувало Express для надання статичних Ассет, я додав додатковий маршрут для обробки запитів і відповіді на них статичним HTML. Даний маршрут, якщо потрібно, робить запит до API і вбиває дані в шаблон, завантажений з файлової системи:
 
 
// ~/server.js
app.get("/", function(req, res) {
  new API(config).for(req.query.line, req.query.station).get(function(err, data) {
    if (err) {
      return res.send(500, "API error");
    }

    new Bootstrap(data).load(function(err, responseHTML) {
      if (err) {
        return res.send(500, "Template error");
      }

      res.send(responseHTML);
    });
  });
});

Модуль шаблонів вкрай простий, він завантажує запитаний файл з диска і замінює в ньому плейсхолдери на передані дані. Немає сенсу використовувати складнішу бібліотеку для шаблонізаціі, так як всі, з чим працюватиме цей шаблонизатор, це два невеликих шматка даних:
 
 
// ~/app/server/template.js
var fs = require("fs");
var path = require("path");

function Template(target) {
  this.target = target;
}

Template.prototype.render = function(data, callback) {
  var fullPath = path.resolve(__dirname, this.target);

  fs.readFile(fullPath, { encoding: "utf8" }, function(err, template) {
    if (err) {
      return callback(err);
    }

    var rendered = template.replace(/\{\{yield:([a-z0-9_]+)\}\}/g, function(match, property) {
      return data[property];
    });

    callback(null, rendered);
  });
};

module.exports = Template;

Через несумісність браузерів, більше не можливо створювати повний HTML документ використовуючи React компоненти. Це може не стосуватися React на серверній стороні, але було б дивно, створювати компоненти, які б відрізнялися в цьому плані на серверної та клієнтської стороні.
 
 
Я скористався можливістю замінити старий і недружній TrackerNet API на новий TfL REST API. Ця заміна трохи зменшує складність роботи, так як уникає роботи з XML, але більш зручні для використання JSON дані з нового API повертаються несортованими і з недоліком деякої корисної інформації. З цієї причини я збудував невеликий проксі для сортування та додавання додаткових корисних даних. У майбутньому тут можна додати прошарок для кешування та запобігання зайвих викликів до API.
 

Висновок

Побудова додатків, які повинні запускатися на сервері і в браузері, було цікавим пригодою. React, це чудовий інструмент для побудови клієнтських додатків і він дає величезну продуктивність, оскільки, з ним дійсно просто будувати динамічні програми. При ретельному плануванні, він допоможе перетворити крихке клієнтське JavaScript додаток в надійний, робочий продукт. Тим не менш, поки-що, я неохоче проповідую ізоморфні JavaScript додатка; зросла складність розробки перетворила побудова додатки Tube Tracker в копітку роботу. Браузерна версія програми зайняла кілька годин розробки, а серверна навіть менше, але структурування і абстрагування коду для роботи додатків, як єдине ціле, зайняло у багато разів більше часу, ніж очікувалося.
 
 image
Відносно невелика кількість проектного коду розділено між двома оточеннями, тільки добре-абстраговані утиліти і React компоненти були перенесені.
 
Безсумнівно, час витрачається на побудову ізоморфних додатків зменшиться, як тільки цей процес стане більш часто зустрічається і з'являться нові шаблони, але зараз мене турбує те, що додаткові витрачені зусилля, це просто примха розробника. Якщо додаток генерується на сервері, є досить функціональним, тоді немає дійсно переконливого аргументу, що юзер експірієнс буде покращено завантаженням та виконанням великого обсягу JavaScript'а, який тільки повторює додаток, який користувач і так має.
 
Зусилля веб-розробників, для повторення поведінки додатків для смартфонів, привели до шаленої революції використовуваних нами технологій і ми багато чому навчилися, у використанні веба, як платформи для доставки всіх видів контенту. Деякі з з'явилися фіч і технік, як ізоморфний JavaScript, дуже цікаві, але побудова нових веб продуктів продовжує стає все складніше.
 
Мені правда подобається React, це чудовий інструмент, але мені він подобається, коли він простий.
 
Ви можете спробувати готове додаток прямо зараз (увага: приклад запущений на безкоштовному акаунті, так що ця посилання може бути нестійкою) або пройти на GitHub, щоб подивитися вихідний код . Будь ласка, коментуйте або твітайте мені , я буду радий отримати відгуки.
  
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.