Больше десяти лет я был PHP-разработчиком, но недавно перешёл на JavaScript, используя его серверные и клиентские возможности. До этого я уже был знаком с JS. Сначала работал с jQuery, потом освоил Angular, и, наконец, начал пользоваться React.
Когда я начинал писать на PHP, я встраивал его в HTML-файлы. Получался не код, а полный бардак. Поэтому, для того, чтобы привести мои разработки в приличный вид, я начал пользоваться фреймворками, в частности, ZF1 и ZF2. Через некоторое время подход, при использовании которого начинают разработку с API, привёл к тому, что у меня оказался сервер, состоящий из сгенерированных REST API и из нескольких сотен строк моего собственного кода.
Так как лишь небольшая и не самая важная часть наших проектов была написана на PHP, возник вопрос о том, можем ли мы от него избавиться. И, если можем, чего нам это будет стоить, и что мы от этого получим. В этом материале я поделюсь опытом с теми, кто, как и я, хочет, понимая, что и зачем он делает, уйти из мира PHP и встать под знамёна JavaScript во всех его проявлениях.
Сегодня я расскажу, в основном, о своём путешествии с серверной стороны PHP на серверную сторону JS в виде Node.js. Здесь я не буду рассказывать о Webpack, React и о других клиентских технологиях JS.
Эволюция стека технологий
Начнём с общей схемы, которая описывает эволюцию используемого нами стека технологий при переходе с PHP на Node.js.
Основные изменения используемого нами стека технологий
Node.js — это основной компонент нашего нового набора технологий. Он отличается высокой производительностью.
Node.js делает своё дело настолько хорошо, что для множества инструментов, ранее написанных на низкоуровневых языках, теперь имеются эквиваленты на JavaScript. Установка обычных программ под используемые нами платформы была такой трудоёмкой, что нам приходилось использовать Ansible для развёртывания рабочей среды. Так как новые инструменты зависят теперь только от Node.js, единственное, что нам приходится устанавливать самостоятельно — это NVM (Node Version Manager) — средство, предназначенное для установки Node.js.
Как правильно устанавливать Node.js с помощью NVM
Когда мы устанавливали Node.js вручную, или используя менеджер пакетов операционной системы, это быстро создало массу проблем при попытках переключаться между версиями Node. В итоге мы пришли к использованию NVM.
Этот инструмент очень легко установить и использовать:
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
nvm install node
nvm use v6.9.1
После его установки нам стали доступны следующие возможности:
- Установка различных версий Node.js на одной системе с использованием одной команды.
- Удобное переключение между установленными версиями Node.js.
Кроме того, что особенно приятно, NVM работает на Linux, на Mac, и на Windows.
JavaScript в 2017-м году
Начиная изучать JavaScript, я воспринимал его как второсортный язык, без которого «к сожалению, не обойтись» при создании динамических веб-сайтов. В результате я никогда и не пытался как следует в нём разобраться. Я учил JavaScript, в основном, по постам в блогах, да по ответам на Stack Owerflow. Теперь я об этом сожалею.
Учите матчасть!
Я был не готов начать использовать современные фреймворки и инструменты JavaScript. В 2017-м JavaScript стал совсем другим благодаря новым возможностям. Среди них можно отметить следующие:
- Классы.
- Промисы (теперь — встроенные в язык).
- Операторы деструктурирования и расширения.
- Модули и загрузчики модулей.
- Структуры Map / Set и их варианты со слабыми ссылками.
- Async / Await.
В итоге я выделил время на то, чтобы проработать кучу материала и как следует изучить JavaScript. Например, сайт Babeljs.io дал мне замечательный обзор того, что такое современное программирование на JavaScript.
Переход с Composer на Yarn
Composer — отличный, но медленный инструмент. У NPM есть тот же недостаток, поэтому мы, вместо него, выбрали Yarn. Это — самая быстрая альтернатива NPM.
В прошлом проекте у нас было около тысячи зависимостей. Команда разработчиков состоит из 10 человек, папка node_modules
меняется, как минимум, 2 раза в день. Прикинем, сколько времени займёт установка пакетов за два месяца по следующей формуле:
(10 разработчиков + 3 окружения) * 2 установки в день * 60 дней * 3 минуты на установку = 78 часов.
Оказывается, две недели рабочего времени было потрачено на наблюдение за ходом загрузки пакетов и на чтение Reddit. Три минуты — достаточно длинный отрезок времени, который способен вылиться в большие потери, но слишком короткий, чтобы, пока идёт установка, можно было бы переключиться на какую-нибудь другую задачу.
Благодаря Yarn нам удалось снизить время установки с 3-х минут до одной минуты. Это позволило сэкономить 46 часов рабочего времени. И, я вам скажу, это — отличный результат.
Создание API в JavaScript
Пожалуй, вместо того, чтобы рассказывать об этом самому, дам слово коду. Вот минимальный пример API, основанного на следующих пакетах:
- Express — легковесный JS-эквивалент Zend Frameword 2 / Symfony.
- Sequelize — альтернатива Doctrine.
- Epilogue — альтернатива Apigility.
const Sequelize = require('sequelize'),
epilogue = require('epilogue'),
express = require('express'),
bodyParser = require('body-parser');
// Определим модели с помощью Sequelize
// Это - эквивалент определения сущностей Doctrine
let database = new Sequelize('database', 'root', 'password');
let User = database.define('User', {
username: Sequelize.STRING
});
// Создадим сервер Express
let app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Подключим Epilogue к Express
epilogue.initialize({
app: app,
sequelize: database
});
// Настроим, с помощью Epilogue, конечные точки ресурсов REST
let userResource = epilogue.resource({
model: User,
endpoints: ['/users', '/users/:id']
});
//Вот и всё, теперь для User доступны запросы GET/POST и DELETE.
app.listen(() => {
console.log('Server started!');
});
Несколько строк кода — и у нас есть REST API, которое поддаётся настройке и расширению.
После того как мы создали более 50 конечных точек API с помощью Apigility, мы поняли две вещи. Во-первых — это возможно, во-вторых — эффективно.
Например, Epilogue без проблем генерирует 10 конечных точек. Имеется и возможность подключать собственный код к стандартному каркасу для поддержки сложных условий, вроде прав пользователей. То, что нельзя сгенерировать автоматически, создаётся в виде простых конечных точек Express RPC с помощью Sequelize.
Не спорю с тем, что Zend Framework 2 обладает гораздо большими возможностями, чем Express. Но Express оказался экономичным и простым решением, которого было достаточно для всех наших нужд. При переходе на него нам не пришлось от чего-либо отказываться.
Flow: спасательный круг для тех, кто утонул в море типов данных
В PHP можно было добавлять подсказки типов данных только тогда, когда это было нужно. Это — одна из моих любимых особенностей PHP. Здесь имеется мощная, но гибкая система типов.
<?php
class UserService {
public function createUser(string $name, User $parent = null, $isAdmin) : User
{
$user = new User($name);
...
return $user
}
}
Многие годы я думал, что нечто подобное в JS невозможно, что единственный выход — переход на TypeScript. Однако, я ошибался.
Я обнаружил библиотеку Flow, так же известную как Flow-type, и смог без проблем добавлять информацию о типах в мои JS-тексты.
Установить библиотеку Flow несложно:
yarn add --save-dev flow-bin
node_modules/.bin/flow init
Так же просто её подключить, добавив одну строчку кода в верхнюю часть файлов, которые необходимо контролировать:
/* @flow */ или // @flow
Затем команда flow check
позволяет получить отчёт, основанный на выведенных типах.
Если ваш проект использует транспилятор, вроде Babel, для поддержки подсказок типов Flow в JS можно добавить новые правила. В результате, с типами можно будет работать так же удобно, как и в PHP.
Подсказки Flow
Падение серверов и мониторинг с помощью PM2
В мире PHP сбой скрипта означает необработанный запрос. В случае с Node.js, если падает сервер, перестаёт функционировать весь веб-сайт. Поэтому процессы необходимо мониторить.
В поиске хорошей системы мониторинга мы перешли с Supervisord на его конкурента, написанного на JavaScript. Вот он:
Мне так нравится PM2, что я просто не могу удержаться от того, чтобы вставить сюда пару сердечек.
PM2 можно устанавливать с помощью Yarn, что само по себе очень хорошо, но и у самого PM2 есть немало сильных сторон. Так, в плане возможностей, он превосходит Supervisord. Он умеет отслеживать нагрузку, которую каждый процесс создаёт на систему, и занимаемую процессом память, его можно настроить так, чтобы он перезапускал процессы при изменении их кода.
Команда pm2 list позволяет вывести сведения обо всех управляемых PM2 процессах
Команда pm2 monit
выдаёт подробный отчёт о том, что происходит с каждым процессом в реальном времени. Кроме того, PM2 упрощает логирование, так как мы можем, без дополнительных усилий, пользоваться стандартными командами console.log()/.warn()/.error()
.
PM2 визуализирует вывод служб и может отслеживать заданные пользователем метрики
Скажу больше. В то время, как сфера возможностей Supervisord ограничена управлением процессами, PM2 может заменить некоторые скрипты, используемые для развёртывания проектов, простыми конфигурационными файлами:
Конфигурационный файл, который позволяет удобно описывать и разворачивать процессы
PM2 для меня — одно из важнейших преимущества перехода на JavaScript. Кроме того, мы можем использовать его для проектов, написанных на любом языке, единственный минус — в подобных случаях, нет столь же тесной интеграции, как при работе с проектами, основанными на JS.
Конфигурирование проектов с помощью .env
В наших проектах Phing использовался для решения трёх задач:
- Конфигурирование проекта.
- Написание скриптов.
- Хранение полезных команд.
В мире JavaScript задачи конфигурирования можно решить с помощью отличной библиотеки DotEnv и файлов .env
. Библиотека позволяет использовать переменные окружения для настройки приложения. Это — один из рекомендованных приёмов из методологии The Twelve-Factor App, которую мы используем на постоянной основе.
Те задачи, которые решались благодаря скриптовым возможностям Phing, больше не требуют от нас использования специальных инструментов. Раньше все наши скрипты были либо привязаны к конфигурированию ПО за пределами мира PHP, такого, как Supervisord, либо могли быть оформлены как независимые сценарии командной строки.
В итоге, единственная роль, которую мог бы выполнять Phing, заключается в хранении команд и создании для них сокращений. Эту задачу отлично решает Yarn (или NPM):
{
"name": "my project name",
"...": "...",
"scripts": {
"release": "standard-version",
"db-reset": "rm -rf data/main.db && sequelize db:migrate && sequelize db:seed:all",
"db-migrate": "sequelize db:migrate",
"dev": "pm2 delete all; pm2 startOrReload ecosystem.config.js && pm2 start react-scripts --name web -- start",
"start": "pm2 startOrReload ecosystem.config.js",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"flow": "flow"
}
}
В итоге мы смогли полностью избавиться от Phing и вызывать наши команды примерно следующим образом:
yarn run db-migrate
Поиск и использование хорошего опенсорсного редактора
Разрабатывая на PHP, я пользовался PhpStorm, платной IDE, так как те, что распространялись бесплатно, либо казались медленными, либо страдали от недостатка плагинов.
В мире JavaScript выбор достойных редакторов кода гораздо богаче. Мы остановились на VS Code. Этот редактор написан на JavaScript, его поддержкой занимается сообщество энтузиастов и Microsoft.
Логотип VS Code
До сих пор всё в VS Code нам нравится. Работает он быстро, обладает замечательной системой автозавершения кода, за ним стоит отличное сообщество.
Здесь мне очень нравится возможность задавать то, какие плагины использовать в проекте, и делиться конфигурациями. Благодаря ей можно подготовить рабочее место программиста буквально за пару кликов.
Избавление от ручного линтинга с помощью Prettier
В PHP у нас была одна замечательная штука. Это — стандарты PSR. Эти стандарты по-настоящему полезны при подготовке правил оформления кода.
Мы настроили наши IDE так, чтобы код соответствовал стандартам PSR 1 и 2. Так как не было функции автоисправления, каждый был ответственен за то, чтобы самостоятельно их применять. Это было не так уж и хорошо.
При переходе на JavaScript неоценимым помощником в этой области стал для нас Prettier. Prettier — это средство форматирования кода, основанное на правилах. Оно, при каждом сохранении файла, удаляет всю самодеятельную стилизацию и приводит код к виду, соответствующему единому стилю.
Prettier в действии
В результате — нет больше споров о стиле кода, нет тренингов по этому направлению, нет потерь времени на то, чтобы объединять файлы, в которых модифицировано только форматирование.
Все в команде используют и любят Prettier. Программисты делают свою работу, а Prettier отвечает за детали форматирования.
Итоги
Рассмотрим плюсы и минусы, с которыми мы столкнулись при переходе с PHP на JS:
Плюсы:
- Наш стек легче устанавливать и развёртывать.
- Клиентский и серверный код написан на одном языке.
- Мы больше не полагаемся на сложные установочные скрипты.
Минусы:
- Потребовалось немало усилий на то, чтобы сформировать набор технологий, соответствующий нашим нуждам.
- Пришлось организовать множество тренингов для того, чтобы изучить современное программирование на JavaScript.
Надо отметить, что после перехода мы смогли быстро генерировать серверные API. Это похоже на то, что мы делали на PHP. Я ни о чём не жалею после перехода и чувствую, что ничего не потерял. Все инструменты, на которые мы переключились, либо являются эквивалентами тех, что мы использовали ранее, либо лучше их.
Если вы подумываете о переходе с PHP на JS, желаем вам успеха и надеемся, что наш рассказ поможет вам сориентироваться в мире JS и выбрать именно то, что вам нужно.
Уважаемые читатели! Если вы пользуетесь PHP, расскажите пожалуйста, как вам живётся, планируете ли переходить на JavaScript?
Автор: ru_vds