10 Node.js Best Practices: Просвещение от Гуру Узла — автор гостя Гостевые посты SitePoint нацелены на привлечение интересного контента от известных писателей и докладчиков веб-сообщества.
В моей предыдущей статье 10 советов, как стать лучшим разработчиком узлов в 2017 году , я представил 10 советов, приемов и приемов Node.js, которые вы можете применить к своему коду сегодня. Этот пост продолжает в том же духе еще 10 лучших практик, которые помогут вам поднять свои навыки работы с Node на следующий уровень. Это то, что мы собираемся охватить:
- Используйте сценарии npm. Прекратите писать сценарии bash, если вы сможете лучше их организовать с помощью сценариев npm и Node. Например,
npm run build
,start
иtest
. Сценарии npm — это единственный источник правды, когда разработчики Node смотрят на новый проект. - Использовать env vars — использовать
process.env.NODE_ENV
, задав его дляdevelopment
илиproduction
. Некоторые фреймворки также будут использовать эту переменную, так что играйте по соглашению. - Поймите цикл
setImmediate()
событий —setImmediate()
не является немедленным, в то время какnextTick()
не является следующим. ИспользуйтеsetImmediate()
илиsetTimeout()
для разгрузки ресурсоемких задач в следующий цикл цикла событий. - Используйте функциональное наследование. Избегайте бессмысленных дебатов и утомительной ловушки отладки и понимания наследования прототипов или классов, просто используя функциональное наследование, как это делают некоторые из наиболее плодотворных участников Node.
- Назовите вещи соответствующим образом — Дайте значимые имена, которые будут служить документацией. Кроме того, пожалуйста, не пишите в верхнем регистре имена файлов, при необходимости используйте тире. Заглавные буквы в именах файлов не просто выглядят странно, но могут вызвать кросс-платформенные проблемы .
- Не используйте JavaScript — ES6 / 7 — жалкое дополнение, родившееся за 6 лет встреч, когда у нас уже был лучший JavaScript, называемый CoffeeScript. Используйте его, если вы хотите, чтобы код доставлялся быстрее и прекратил тратить время на обсуждение
var
/const
/let
, точек с запятой,class
и других аргументов. - Предоставить собственный код — при использовании транспортеров фиксируйте собственный код JS (результат сборок), чтобы ваши проекты могли выполняться без сборок.
- Используйте gzip — Дух!
npm i compression -S
и логическое ведение журнала — не слишком много или мало в зависимости от среды.npm i morgan -S
- Увеличьте масштаб. Начните думать о кластеризации и услугах без сохранения состояния с самого первого дня разработки Node. Используйте управление кластером pm2 или strongloop
- Запросы к кешу. Получите максимальную отдачу от ваших серверов Node, спрятав их за статическим файловым сервером, таким как nginx, и / или кеш уровня запросов, например, Varnish Cache и CDN.
Итак, давайте рассмотрим и рассмотрим каждый из них в отдельности. А не ___ ли нам?
Используйте сценарии npm
Сейчас почти стандартно создавать сценарии npm для сборок, тестов и, самое главное, для запуска приложения. Это первое место, на которое смотрят разработчики Node, когда сталкиваются с новым проектом Node. Некоторые люди ( 1 , 2 , 3 , 4 ) даже отказались от Grunt, Gulp и им подобных для более низкого, но более надежного сценария npm. Я могу полностью понять их аргумент. Учитывая, что скрипты npm имеют хуки до и после, вы можете достичь очень сложного уровня автоматизации:
"scripts": { "preinstall": "node prepare.js", "postintall": "node clean.js", "build": "webpack", "postbuild": "node index.js", "postversion": "npm publish" }
Часто, когда вы разрабатываете для внешнего интерфейса, вы хотите запустить два или более процесса наблюдения, чтобы пересоздать ваш код. Например, один для веб-пакета и другой для nodemon. Вы можете сделать это с &&
так как первая команда не выдаст приглашение. Однако есть удобный модуль, который вызывается одновременно, который может порождать несколько процессов и запускать их одновременно.
Кроме того, локально установите инструменты командной строки dev, такие как webpack, nodemon, gulp, Mocha и т. Д., Чтобы избежать конфликтов. Например, вы можете указать ./node_modules/.bin/mocha
или добавить эту строку в свой профиль bash / zsh (PATH!):
export PATH="./node_modules/.bin:$PATH"
Используйте Env Vars
Используйте переменные среды даже на ранних этапах проекта, чтобы гарантировать отсутствие утечки конфиденциальной информации, и просто правильно построить код с самого начала. Более того, некоторые библиотеки и фреймворки (я точно знаю, что это делает Express) будут извлекать информацию, такую как NODE_ENV
чтобы изменить свое поведение. Установите его на production
. Установите свои значения MONGO_URI
и API_KEY
. Вы можете создать файл оболочки (например, start.sh
) и добавить его в .gitignore
:
NODE_ENV=production MONGO_URL=mongo://localhost:27017/accounts API_KEY=lolz nodemon index.js
У Nodemon также есть файл конфигурации, куда вы можете поместить ваши env vars ( пример ):
{ "env": { "NODE_ENV": "production", "MONGO_URL": "mongo://localhost:27017/accounts" } }
Понять цикл событий
Мощный и умный цикл обработки событий делает Node таким быстрым и блестящим, используя все время, которое было бы потрачено впустую в ожидании выполнения задач ввода и вывода. Таким образом, Node отлично подходит для оптимизации систем ввода-вывода.
Если вам нужно выполнить что-то интенсивное использование процессора (например, вычисление, хэширование паролей или сжатие), то в дополнение к порождению новых процессов для этих задач CPU, вы можете изучить setImmediate()
задачи с помощью setImmediate()
или setTimeout()
— код в их обратных вызовах будет продолжен в следующем цикле цикла обработки событий. nextTick()
работает в том же цикле вопреки названию. Argh!
Вот диаграмма от Берта Белдера, который работал над циклом событий. Он четко знает, как работает цикл обработки событий!
Использовать функциональное наследование
JavaScript поддерживает прототип наследования, когда объекты наследуются от других объектов. Оператор class
также был добавлен к языку с ES6. Однако это явно сложно по сравнению с функциональным наследованием. Большинство узловых гуру предпочитают простоту последних. Он реализован с помощью простого шаблона фабрики функций и НЕ требует использования prototype
, new
или this
. При обновлении прототипа не возникает никаких явных эффектов (что также приводит к изменению всех экземпляров), поскольку при функциональном наследовании каждый объект использует свою собственную копию методов.
Рассмотрим код от TJ Holowaychuk, плодовитого гения, стоящего за Express, Mocha, Connect, Superagent и десятками других модулей Node. Express использует функциональное наследование ( полный исходный код ):
exports = module.exports = createApplication; // ... function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
Чтобы быть объективным, основные модули Node часто используют наследование прототипов. Если вы следуете этому шаблону, убедитесь, что вы знаете, как он работает. Вы можете прочитать больше о шаблонах наследования JavaScript здесь.
Назовите вещи соответственно
Это очевидно. Добрые имена служат документацией. Какой из них вы бы предпочли?
const dexter = require('morgan') // ... app.use(dexter('dev')) // When is the next season?
Я понятия не имею, что делает dexter
, когда я смотрю только на app.use()
. Как насчет другого более значимого имени:
const logger = require('morgan') // ... app.use(logger('dev')) // Aha!
Точно так же имена файлов должны правильно отражать назначение кода внутри. Если вы посмотрите на папку lib
в Node ( ссылка на GitHub ), в которой все основные модули связаны с платформой, вы увидите четкое именование файлов / модулей (даже если вы не очень хорошо знакомы со всеми основными модулями). ):
events.js fs.js http.js https.js module.js net.js os.js path.js process.js punycode.js querystring.js
Внутренние модули отмечены подчеркиванием ( _debugger.js
, _http_agent.js
, _http_client.js
), так же, как методы и переменные в коде. Это помогает предупредить разработчиков о том, что это внутренний интерфейс, и если вы используете его, вы сами по себе — не жалуйтесь, если он подвергся рефакторингу или даже удален.
Не используйте JavaScript
А? Вы только что прочитали это правильно? Но какого черта? Да. Правильно. Даже с ES6 и двумя функциями, добавленными ES2016 / ES7, JavaScript все еще имеет свои особенности. Есть и другие варианты, кроме JavaScript, которые вы или ваша команда можете получить с очень небольшой настройкой. В зависимости от уровня знаний и характера приложения вам может быть лучше использовать TypeScript или Flow, которые обеспечивают строгую типизацию. На другом конце спектра есть Elm или ClojureScript, которые являются чисто функциональными. CoffeeScript — еще один отличный и проверенный в бою вариант. Вы также можете взглянуть на Dart 2.0 .
Когда все, что вам нужно, это всего лишь несколько макросов (макросы позволяют вам создать именно тот язык, который вы хотите), а не целый новый язык, тогда рассмотрите Sweet.js, который сделает именно это — позволит вам написать код, который генерирует код.
Если вы идете не по JavaScript, пожалуйста, по-прежнему включайте скомпилированный код, потому что некоторые разработчики могут не понимать ваш язык достаточно хорошо, чтобы правильно его построить. Например, VS Code является одним из крупнейших проектов TypeScript, возможно, после Angular 2, а Code использует TypeScript для исправления основного модуля Node с типами. В vscode/src/vs/base/node/
VS Code ( ссылка ) вы можете увидеть знакомые имена модулей, такие как crypto
, process
и т. Д., Но с расширением ts
. В репо есть и другие файлы. Однако они также включали vscode/build
с собственным кодом JavaScript.
Знай Экспресс Middleware
Экспресс это отличная и очень зрелая структура. Это великолепие происходит от того, что позволяет множеству других модулей настраивать его поведение. Таким образом, вам необходимо знать наиболее используемое промежуточное программное обеспечение и знать, как его использовать . Так почему бы не взять мою шпаргалку Express ? У меня есть основные модули промежуточного программного обеспечения, перечисленные там. Например, npm i compression -S
даст снижение скорости загрузки путем дефлирования ответов. logger('tiny')
или logger('common')
предоставит меньше (dev) или больше (prod) журналов соответственно.
Увеличить масштаб
Node хорош в асинхронном режиме благодаря неблокирующему вводу-выводу и поддерживает этот простой способ асинхронного кодирования простым, потому что есть только один поток. Это возможность начать масштабирование на ранней стадии, возможно, даже с первых строк кода. Есть модуль cluster
ядра, который позволит вам масштабировать вертикально без особых проблем. Тем не менее, еще лучше было бы использовать такой инструмент, как pm2 или кластерный контроль StrongLoop .
Например, вот как вы можете начать работу с pm2:
npm i -g pm2
Затем вы можете запустить четыре экземпляра одного и того же сервера:
pm2 start server.js -i 4
Для Docker в pm2 версии 2+ есть pm2-docker
. Итак, ваш Dockerfile может выглядеть так:
# ... RUN npm install pm2 -g CMD ["pm2-docker", "app.js"]
Официальный образ Alpine Linux pm2 находится в Docker Hub .
Запросы кеша
Это лучшая практика DevOps, которая позволит вам получить больше сока из ваших экземпляров Node (вы получите больше одного с pm2 или подобным, см. Выше). Нужно позволить серверам Node выполнять такие приложения, как отправка запросов, обработка данных и выполнение бизнес-логики, а также пересылать трафик на статические файлы на другой веб-сервер, такой как Apache httpd или Nginx. Опять же, вы, вероятно, должны использовать Docker для настройки:
FROM nginx COPY nginx.conf /etc/nginx/nginx.conf
Мне нравится использовать Docker compose, чтобы несколько контейнеров (nginx, Node, Redis, MongoDB) работали друг с другом. Например:
web: build: ./app volumes: - "./app:/src/app" ports: - "3030:3000" links: - "db:redis" command: pm2-docker app/server.js nginx: restart: always build: ./nginx/ ports: - "80:80" volumes: - /www/public volumes_from: - web links: - web:web db: image: redis
-web: build: ./app volumes: - "./app:/src/app" ports: - "3030:3000" links: - "db:redis" command: pm2-docker app/server.js nginx: restart: always build: ./nginx/ ports: - "80:80" volumes: - /www/public volumes_from: - web links: - web:web db: image: redis
по темеweb: build: ./app volumes: - "./app:/src/app" ports: - "3030:3000" links: - "db:redis" command: pm2-docker app/server.js nginx: restart: always build: ./nginx/ ports: - "80:80" volumes: - /www/public volumes_from: - web links: - web:web db: image: redis
по темеweb: build: ./app volumes: - "./app:/src/app" ports: - "3030:3000" links: - "db:redis" command: pm2-docker app/server.js nginx: restart: always build: ./nginx/ ports: - "80:80" volumes: - /www/public volumes_from: - web links: - web:web db: image: redis
Резюме
В наше время программного обеспечения с открытым исходным кодом нет никаких оправданий, чтобы не учиться на проверенном и проверенном коде, который открыт. Вам не нужно быть во внутреннем кругу, чтобы войти. Обучение никогда не останавливается, и я уверен, что скоро у нас будут разные лучшие практики, основанные на неудачах и успехах, которые мы испытаем. Они гарантированы.
Наконец, я хотел написать о том, как программное обеспечение пожирает мир и как JavaScript пожирает его… есть такие замечательные вещи, как ежегодные стандартные выпуски, множество модулей npm, инструменты и конференции… но вместо этого я закончу словом осторожности
Я вижу, как все больше и больше людей гоняются за следующей новой структурой или языком. Это синдром блестящего объекта. Они изучают новую библиотеку каждую неделю и новые рамки каждый месяц. Они навязчиво проверяют Twitter, Reddit, Hacker News и JS Weekly. Они используют подавляющий уровень активности в мире JavaScript, чтобы откладывать. У них есть пустые публичные истории GitHub.
Учиться новым вещам — это хорошо, но не путайте это с фактическим созданием вещей. Что важно и что платит ваша зарплата, так это на самом деле создание вещей. Остановись над инжинирингом . Вы не создаете следующий Facebook. Обещания против генераторов и асинхронное ожидание — это спорный вопрос для меня, потому что к тому времени, когда кто-то ответил на обсуждение в обсуждении, я уже написал свой обратный вызов (и использовал CoffeeScript, чтобы сделать это в 2 раза быстрее, чем в простой ES5 / 6/7! ).
Последняя лучшая практика — использование лучших практик, а лучшая из лучших — овладение основами. Читайте исходный код, пробуйте что-то новое в коде и, самое главное, пишите тонны кода самостоятельно. Теперь, на этом этапе, прекратите чтение и отправьте код, который имеет значение!
И на случай, если этого поста не достаточно, вот еще немного прочтения о лучших практиках Node:
- https://blog.risingstack.com/nodejs-at-scale-npm-best-practices
- https://devcenter.heroku.com/articles/node-best-practices
- https://blog.risingstack.com/node-js-best-practices
- https://expressjs.com/en/advanced/best-practice-performance.html
- https://www.codementor.io/nodejs/tutorial/nodejs-best-practices