Skip to content

Latest commit

 

History

History
222 lines (142 loc) · 16 KB

03-express-nodemon-pm2_ru.md

File metadata and controls

222 lines (142 loc) · 16 KB

03 - Express, Nodemon и PM2

Код для этой главы доступен тут.

В этом разделе мы создадим сервер, который будет генерировать наше веб приложение. Также мы настроим для этого сервера режимы разработки и production.

Express

Express определенно наиболее популярный фреймворк для веб приложений под Node. У него очень простой и минимальный API, а его возможности могут быть расширены с помощью промежуточного ПО (middleware).

Давайте настроим минимальный сервер Express, выдающий HTML страницу с минимальным CSS.

  • Удалите все внутри src

Создайте следующие файлы и папки:

  • Создайте файл public/css/style.css содержащий:
body {
  width: 960px;
  margin: auto;
  font-family: sans-serif;
}

h1 {
  color: limegreen;
}
  • Создайте пустую папку src/client/.

  • Создайте пустую папку src/shared/.

Эта папка - место в которое мы поместим изоморфный / универсальный JavaScript код - файлы которые будут использованы как на клиенте, так и на сервере. Отличный пример использования общего кода - маршруты (routes), как вы увидите дальше в этом руководстве, когда мы будем использовать асинхронный вызов. Пока что мы просто разместим тут несколько конфигурационных констант в качестве примера.

  • Создайте файл src/shared/config.js, содержащий:
// @flow

export const WEB_PORT = process.env.PORT || 8000
export const STATIC_PATH = '/static'
export const APP_NAME = 'Hello App'

Если процесс Node, запускающий ваше приложение содержит переменную окружения process.env.PORT (например, в случае, если вы публикуете на Heroku), она будет задавать порт. В противном случае, по умолчанию будет использоваться 8000.

  • Создайте файл src/shared/util.js, содержащий:
// @flow

// eslint-disable-next-line import/prefer-default-export
export const isProd = process.env.NODE_ENV === 'production'

Это простая утилита для проверки запущены ли мы в режиме production или нет. Комментарий // eslint-disable-next-line import/prefer-default-export нужен изза того, что у нас только один именованный экспорт в это файле. Вы можете его убрать как только добавите сюда экспорт других переменных.

  • Выполните yarn add express compression

compression - это промежуточное ПО для Express активирующее Gzip сжатие на сервере.

  • Создайте файл src/server/index.js содержащий:
// @flow

import compression from 'compression'
import express from 'express'

import { APP_NAME, STATIC_PATH, WEB_PORT } from '../shared/config'
import { isProd } from '../shared/util'
import renderApp from './render-app'

const app = express()

app.use(compression())
app.use(STATIC_PATH, express.static('dist'))
app.use(STATIC_PATH, express.static('public'))

app.get('/', (req, res) => {
  res.send(renderApp(APP_NAME))
})

app.listen(WEB_PORT, () => {
  // eslint-disable-next-line no-console
  console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : '(development)'}.`)
})

Здесь ничего особенного, это практически 'Hello World' для Express плюс несколько дополнительных импортов. Мы здесь используем две отдельных директории для статических файлов. dist для генерируемых и public для декларируемых.

  • Создайте файл src/server/render-app.js содержащий:
// @flow

import { STATIC_PATH } from '../shared/config'

const renderApp = (title: string) =>
`<!doctype html>
<html>
  <head>
    <title>${title}</title>
    <link rel="stylesheet" href="${STATIC_PATH}/css/style.css">
  </head>
  <body>
    <h1>${title}</h1>
  </body>
</html>
`

export default renderApp

Возможно, вам привычно использовать шаблонизаторы при работе с back-end? Что ж, теперь их можно считать довольно устаревшимы с тех пор, как JavaScript стал поддерживать шаблонные строки. Здесь мы создали функцию, которая принимает в качестве параметра title и вставляет это значение в тэги title и h1, возвращая строку с полноценной HTML страницей. Мы также используем константу STATIC_PATH для задания базового пути для всех наших статических элементов.

Подсветка HTML синтаксиса в шаблонных строках для Atom (не обязательное)

В зависимости от используемого текстового редактора, возможна подсветка синтаксиса HTML кода внутри шаблонных строк. В Атоме, если вы добавите префикс html к шаблонной строке (или любой другой префикс, заканчивающийся на html, как ilovehtml), то содержимое этой строки автоматически будет подсвечиваться. Я иногда использую html тэг из библиотеки common-tags, чтобы воспользоваться данной возможностью:

import { html } from `common-tags`

const template = html`
<div>Wow, colors!</div>
`

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

В любом случае вернемся к нашему проекту.

  • В package.json измените скрипт start таким образом: "start": "babel-node src/server",

🏁 Запустите yarn start и откройте localhost:8000 в браузере. Если все заработало как и ожидалось, то вы увидете пустую страницу с надписями "Hello App" в названии вкладки и на зеленом заголовке страницы.

Примечание: Некоторые процессы (обычно процессы, ожидающие своего завершения, как, например, сервер) не позволяют вам вводить команды в терминале пока они не завершатся. Чтобы прервать подобный процесс и получить обратно приглашение к вводу, нажмите Ctrl+C. Как вариант, вы можете открыть еще одну вкладку с терминалом, если хотите, чтобы процесс работал, пока вы вводите команды. Вы также можете запустить эти процессы в фоне, но это вне рамок данного руководства.

Nodemon

💡 Nodemon - утилита для автоматического перезапуска сервера Node при изменении файлов в директории.

Мы будем использовать Nodemon в режиме разработки

  • Запустите yarn add --dev nodemon

  • Измените scripts так, чтобы:

"start": "yarn dev:start",
"dev:start": "nodemon --ignore lib --exec babel-node src/server",

Теперь start лишь указатель на другую задачу - dev:start. Это дает нам уровень абстракции, позволяющий настраивать какая задача будет выполняться по умолчанию.

В dev:start, мы устанавливаем флаг --ignore lib для того, чтобы не перезапускать сервер, когда изменения происходят в директории lib. У вас пока еще нет этой директории, но мы создадим ее в следующем разделе этой главы. Так что скоро это понадобится. Обычно Nodemon запускает бинарники node. В нашем случае, поскольку мы используем Babel, мы, вместо этого, указали Nodemon запускать babel-node. Таким образом, мы сделали доступным весь наш ES6/Flow код.

🏁 Запустите yarn start и откройте localhost:8000. Двигаемся дальше и изменим константу APP_NAME в src/shared/config.js, что должно вызвать перезапуск сервера в терминале. Обновите страницу, чтобы увидеть измененный заголовок. Заметьте, что этот автоматический рестарт сервера отличается от Hot Module Replacement, при котором компоненты обновляются на странице в реальном времени. Здесь нам по прежнему требуется ручное обновление, но по крайней мере не нужно убивать процесс и вручную перезапускать сервер, чтобы увидеть изменения. Hot Module Replacement будет представлен в следующей главе.

PM2

💡 PM2 - это менеджер процессов для Node, обеспечивающий жизнеспособность вашего приложения и предлагающий тонны возможностей по управлению и мониторингу.

Мы будем использовать PM2 в режиме production

  • Выполните yarn add --dev pm2

В production вы хотите, чтобы сервер был настолько производительным, насколько это возможно. babel-node начинает процесс транспиляции всех файлов при каждом перезапуске, чего вы бы хотели избежать в production. Нам нужно, чтобы Babel выполнил всю эту работу заранее, и сервер выдавал обычные старые предкомпилированные ES5 файлы.

Одной из основных возможностей Babel является способность взять папку с ES6 кодом (обычно src) и транспилировать его в папку с ES5 кодом (обычно lib).

Поскольку папка lib автогенерируется, хорошей практикой будет очищать ее перед каждым новым построением, поскольку она может содержать нежелательные старые файлы. rimraf – простой лаконичный пакет, для удаления файлов с кроссплатформенной поддержкой.

  • Запустите yarn add --dev rimraf

Давайте добавим следующую задачу prod:build в package.json:

"prod:build": "rimraf lib && babel src -d lib --ignore .test.js",
  • Запустите yarn prod:build. Это должно сгенерировать папку lib, содержащую транспилированный код, за исключением файлов, заканчивающихся на .test.js (заметьте, что файлы .test.jsx также игнорируются с помощью этого параметра).

  • Добавьте /lib/ в .gitignore

Последняя вещь: Мы собираемся передать переменную окружения NODE_ENV в исполняемый файл PM2. В Unix, вы бы сделали это через NODE_ENV=production pm2, но Windows использует другой синтаксис. Мы воспользуемся небольшим пакетом cross-env, чтобы заставить этот синтаксис работать также и для Windows.

  • Запустите yarn add --dev cross-env

Обновим package.json так:

"scripts": {
  "start": "yarn dev:start",
  "dev:start": "nodemon --ignore lib --exec babel-node src/server",
  "prod:build": "rimraf lib && babel src -d lib --ignore .test.js",
  "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs",
  "prod:stop": "pm2 delete server",
  "test": "eslint src && flow && jest --coverage",
  "precommit": "yarn test",
  "prepush": "yarn test"
},

🏁 Запустите yarn prod:build, а затем yarn prod:start. PM2 должен показать активный процесс. Зайдите на http://localhost:8000/ в браузере и вы должны увидеть наше приложение. Терминал должен выдать лог: "Server running on port 8000 (production).". Заметьте, что PM2 запускает процессы в фоне. Если вы нажмете Ctrl+C, это прервет команду pm2 logs, которая была последней в цепочке после prod:start, но сам сервер по прежнему должен генерировать страницы. Если вам нужно остановить сервер, наберите yarn prod:stop.

Теперь, когда у нас есть задача prod:build, было бы здорово проверять все ли работает хорошо перед тем как закачивать код в репозиторий. Поскольку, возможно не требуется запускать его перед каждым коммитом, я предлагаю добавить это в задачу prepush:

"prepush": "yarn test && yarn prod:build"

🏁 Запустите yarn prepush или просто начните загружать файлы (push), чтобы запустить этот процесс.

Примечание: У нас пока нет никаких тестов, так что Jest пожалуется на это. Пока что проигнорируйте это.

Следующий раздел: 04 - Webpack, React, HMR

Назад в предыдущий раздел или содержание.