Rugby News Board — упомянутый в названии побочный проект, представляющий собой простой новостной сайт о регби на китайском языке. Я сделал это, потому что нет новостного сайта регби на китайском языке, а я люблю регби.

Rugby News Board был сначала построен на Rails с простыми новостными каналами. С помощью Rails я смог сделать прототип за 2 дня. Я написал таблицу news с помощью ORM и SQLite. Он работал на Linode VPS, и я сам занимался DevOps.

Героку

Поскольку я прочитал статью от Unsplash Масштабирование Unsplash с помощью небольшой команды, я понял, что мне следует мигрировать с VPS на PaaS, чтобы избежать ненужных затрат на инфраструктуру. Я продолжал оценивать контейнеры и другие технологии на VPS, но это не было ни прибыльным, ни продуктивным для проекта.

В итоге я начал переходить на Heroku, так как стремился к большей эффективности, надежности и удобству. Процесс не требовал особых усилий. Единственное серьезное изменение в моем приложении Rails заключается в том, что база данных была превращена в Postgres.

Изоморфный JavaScript

Rails является гибким как внутренним, так и внешним интерфейсом, с поддержкой компонентов для всего, и это идеальная среда MVC для многостраничных приложений. Однако за MVVM будущее, например. Реагировать. Это немного более разумный, элегантный и выразительный способ сделать данные реактивными, чем модифицировать DOM с помощью jQuery. Более того, я просто хочу идти в ногу с современной разработкой внешнего интерфейса.

Кстати, я чувствую себя довольно странно, используя CoffeeScript как часть стеков Rails. Находясь в окружении ванильного JavaScript, EcmaScript всех версий и TypeScript, я не заинтересован в изучении непопулярной замены JavaScript, которую некоторые чуваки назвали мертвой.

Я попытался использовать React с реактивными рельсами для создания системы перевода имен Rugby. Он просто основан на Rails, и мы можем использовать компонент React с тегом react_component и просто беспрепятственно передавать данные (объекты Ruby) компоненту.

<div class="section-item"> 
  <%= react_component "RugbyDictQuery", {title: "Rugby Dictionary"} %> 
</div>

react-rails — отличное связующее звено между Rails и React. Но тенденция изоморфна React-приложениям, поскольку интерфейс и серверная часть обслуживают только API. Я прочитал несколько статей и слайдов от компаний, использующих как Rails, так и React, например, Эволюция внешнего интерфейса Airbnb и снова Масштабирование Unsplash с небольшой командой.

Они оба предпочитали создавать изоморфное приложение JavaScript с помощью Node и React. Я следил за Unsplash, потому что мы используем одни и те же PaaS и фреймворки.

Реагировать приложение

Сначала я настроил свой проект на JavaScript Stack from Scratch. Я не буду выкладывать здесь много кодов, так как это рутинная работа с React. Единственная досада в том, что я забыл использовать ESLint с самого начала. В результате мне пришлось часами исправлять более 200 проблем.

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

Webpack с несколькими записями

Примеры и шаблоны обычно представлены в одной HTML-форме. Однако в моем приложении есть две страницы. Один index.html для запросов пользователей, другой admin.html для редакторов. Оба они должны быть оснащены HMR (горячая замена модуля).

Эта статья великолепна, как говорит название Истинно множественные записи с помощью Webpack.

const path = require('path'); 
const webpack = require('webpack'); 
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { 
  entry: { 
    index: [ 
      'webpack-hot-middleware/client', 
      './client/index.jsx', 
    ], 
    admin: [ 
      'webpack-hot-middleware/client', 
      './client/admin.jsx', 
    ], 
  }, 
  module: { 
    // loaders ... 
  }, 
  resolve: { 
    extensions: ['.js', '.jsx'], 
  }, 
  output: { 
    path: path.resolve(__dirname, '..', 'dist'), 
    filename: '[name]/bundle.js', publicPath: '/', 
  }, 
  plugins: [ 
    new HtmlWebpackPlugin({ 
      template: './client/index.html', 
      filename: 'index.html', 
      inject: 'body', 
      chunks: ['index'], 
      hash: true, 
    }), 
    new HtmlWebpackPlugin({ 
      template: './client/admin.html', 
      filename: 'admin.html', 
      inject: 'body', 
      chunks: ['admin'], 
      hash: true, 
    }), 
    new webpack.HotModuleReplacementPlugin(), 
    new webpack.NoEmitOnErrorsPlugin(), 
  ], 
  devServer: { historyApiFallback: true, }, 
};

Сервер разработки

Чтобы получить хороший опыт разработчика, важно настроить среду отладки в реальном времени для разработки. Я был готов настроить сервер разработки для своего изоморфного приложения с помощью webpack-dev-middleware и webpack-hot-middleware.

const webpack = require('webpack'); 
const webpackConfig = require('../config/webpack.dev.config.js'); const compiler = webpack(webpackConfig); 
const webpackDevMiddleware = require('webpack-dev-middleware'); 
const webpackHotMiddleware = require('webpack-hot-middleware');
// Dev
app.use(webpackDevMiddleware(compiler, { 
  noInfo: true, 
  publicPath: webpackConfig.output.publicPath, 
}));
// HMR 
app.use(webpackHotMiddleware(compiler, { 
  log: console.log, 
  path: '/__webpack_hmr', 
  heartbeat: 10 * 1000, 
}));

Однако с несколькими страницами все усложнилось. Казалось, что webpack-dev-middleware никогда не собирает файлы в выходной путь.

index.html всегда был доступен, а другая страница - admin.html, возможно, находилась в какой-то черной дыре. Я решил это обслуживать файлы из компилятора.

app.get('/admin', (req, res) => { 
  const filename = path.join(compiler.outputPath, 'admin.html');  
  compiler.outputFileSystem.readFile(filename, (err, result) => { 
    res.set('content-type', 'text/html'); 
    res.send(result); 
    res.end(); 
  }); 
});

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

Сборка после развертывания

Развернуть на Heroku несложно, но сначала мне пришлось закоммитить папку dist в кодовую базу, что было очень тяжело (сотни килобайт на каждое нажатие). Затем я нашел хук heroku-postbuild, с помощью которого развертывание может запускать сборку Webpack.

Итак, добавим heroku-postbuild к package.json:

"heroku-postbuild": "webpack -p --config ./config/webpack.prod.config.js"

Совет. Мы должны убедиться, что процесс сборки работает, прежде чем отправлять коды. В противном случае развертывание завершится ошибкой.

Оптимизация и предварительная обработка

После запуска нового приложения Node/React я с трудом мог игнорировать долгий промежуток между полученным ответом и тем, что наконец увидел страницу. Heroku обычно лагает в Китае из-за геолокации и отличного брандмауэра. Я приписывал это этой причине, пока не обнаружил, что bundle.js составляет 1,8 мб.

Причина в том, что производственная конфигурация Webpack была неправильной, я исправил это, добавив UglifyJsPlugin, и теперь он составляет 700 КБ.

plugins: [ 
  HtmlWebpackPluginConfig, 
  new webpack.NoEmitOnErrorsPlugin(), 
  new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
  new webpack.optimize.OccurrenceOrderPlugin(), 
  new webpack.optimize.UglifyJsPlugin({ 
    compress: { screw_ie8: true, warnings: false, }, 
    mangle: { screw_ie8: true, }, 
    output: { comments: false, screw_ie8: true, }, 
  }), 
],

Даже 700 кб — это довольно большой размер. И тогда мне пришла в голову идея использовать Preact, быструю альтернативу React 3kb с тем же ES6 API, что подтверждает мой друг-эксперт по фронтенду @yanni4night.

У меня не было много кода, поэтому я использую Preact напрямую, без использования preact-compat, чтобы сделать его совместимым с React.

import { h, Component } from 'preact';

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

react-router был заменен на preact-router, который стал еще более удобным для пользователя. Параметры просто передаются в props:

Маршрутизатор:

<EventPage path="/event/:name" />

Компонент EventPage:

export default class EventPage extends Component {
  constructor(props) { 
    super(props); 
    this.state = { 
      eventName: props.name, 
      pageNum: props.page || 0, 
    }; 
  } 
}

После оптимизации он составляет всего 364 КБ, и обычно загрузка приложения занимает менее 2 секунд. Использование Preact — хороший выбор, в то время как мы можем получить преимущество React и извлечь выгоду из легкости.

Вывод

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

Первоначально опубликовано на crispgm.com.