Olmsted

最佳Node实践之实用十式: Node大师带来的启迪

Olmsted · 2017-02-02翻译 · 448阅读 原文链接

A Street Fighter-like character leaping into the air, gathering energy between his hands - level up your Node skills

最佳Node实践之实用十式:受我们的客邀作者,Node大师Azat Mardan的启发。 SitePoint 的客座发帖旨在带给大家web社区里杰出的作者和宣讲者的优秀内容。

关于作者的其它文章:

在我之前的文章: 10 Tips to Become a Better Node Developer in 2017中,介绍了10个Node.js的技巧,现在你已经可以应用到自己的代码中。而这篇文章与此前的文章一脉相承,10个进一步的最佳实践技巧帮助你的Node技能更上一层。以下是我们将涉及到的内容:

  1. 使用 npm scripts — 那些bash脚本,当可以使用npm scripts和Node更好地组织它们的时候,就别再用写的了。 例如,npm run buildstarttest。npm scripts 就像Node开发人员接触一个新的项目的时去获取真相的唯一途径。

  2. 使用环境变量 —通过将process.env.NODE_ENV设置为developmentproduction来使用。 一些框架将也会使用这个变量,所以按游戏规则进行吧。

  3. 理解事件循环setImmediate()不是立即的,而nextTick()不是指下一个。 使用setImmediate()setTimeout()将CPU密集型任务卸放到下一个事件循环周期。

  4. 使用 functional inheritance — 避免陷入无脑的纷争,或者那些脑细胞流失的调试陷阱和对原型继承或类的理解过程,而是只使用functional inheritance,就像某些多产的Node贡献者那样。

  5. Name things appropriately — 提供有意义的名称,将来可以作为文档使用。 此外,请不要将文件名大写,如果需要可以使用中划线。 大写的文件名不只是看起来奇怪,而且会导致跨平台的问题

  6. 考虑不使用JavaScript — ES6/7是经过六年会议后诞生的可悲的补丁,而同时已经有了更好的JavaScript——CoffeeScript。如果你希望代码交付更快,并且不再浪费时间在var/const/let、分号、class和其它方面的争议上。

  7. 提交源生代码 — 当使用转译器时,提交原生JS代码(build后的结果),所以你的项目不经过build就可以运行。

  8. 使用 gzip — 额。。。 npm i compression -S 和完整的日志记录 — 不多也不少地依赖于环境. npm i morgan -S

  9. 放大 — 从Node开发的第一天开始考虑聚集和无状态服务。使用pm2或者strongloop’s cluster control。

  10. 缓存请求 — 通过将它们藏在在静态文件服务器(如nginx)或者请求级缓存(如Varnish Cache和CDN缓存)的后面,可以从Node服务器获得最大的关联。

那么让我们一个个分开看看他们吧。走起!

使用 npm Scripts

创建并使用npm scripts 对应用进行构建、测试以及最重要的运行,几乎已经是标准了。这是Node开发人员在遇到一个新的Node项目时首先看到的。有人 (1, 2, 3, 4) 甚至已经放弃使用Grunt、Gulp,喜欢更低级但更可靠的npm script。我完全可以理解他们的观点。考虑到npm脚本有前缀和后缀,你可以达到非常复杂的自动化水平:

"scripts": {
  "preinstall": "node prepare.js",
  "postintall": "node clean.js",
  "build": "webpack",
  "postbuild": "node index.js",
  "postversion": "npm publish"
}

通常在前端开发中,需要运行两个以上的监视进程,以此来重建代码。例如,一个进程用于webpack,另一个用于nodemon。为此,你可以使用&&,因为第一行不会输出提示。不过,有一个很便捷的模块叫做 concurrently ,它可以产生多个进程,并同时运行它们。

另外,请在局部使用命令行安装的开发工具,例如:webpack,nodemon,gulp,Mocha等。例如你的安装可以指向./node_modules/.bin/mocha或者将此行命令加入到你的bash/zsh文件夹(PATH!)

export PATH="./node_modules/.bin:$PATH"

使用 Env Vars (环境变量)

即使在项目的早期阶段也要使用环境变量,以确保没有泄漏敏感信息,从一开始就得正确地构建代码。此外,一些库和框架(我确定Express就是这么做的)将使用如NODE_ENV信息,来修正它们的行为。 将其设置为production。 设置你的MONGO_URIAPI_KEY值。 您可以创建一个shell文件(例如start.sh')并将其添加到.gitignore`:

NODE_ENV=production MONGO_URL=mongo://localhost:27017/accounts API_KEY=lolz nodemon index.js

Nodemon还有一个配置文件,你可以在其中设置env变量: (例子戳此处):

{
  "env": {
    "NODE_ENV": "production",
    "MONGO_URL": "mongo://localhost:27017/accounts"
  }
}

理解事件循环

正是强大巧妙的事件循环使Node如此高速和辉煌,充分利用所有的时间,这些事件本会浪费在等待输入和输出任务完成。因此,Node非常适合优化绑定I/O操作的系统。

如果你需要执行某些CPU密集型(例如,计算,哈希密码或压缩),那么除了为这些CPU任务产生新进程之外,你可能想到使用setImmediate()setTimeout()——它们回调函数中的代码会在下一个事件循环周期中继续运行。 nextTick()工作在同一个周期,与名称相反。 Argh!

这是Bert Belder提供的图示,他从事事件循环方面的工作,清楚知道事件循环如何工作!

The event loop

使用Functional Inheritance

(译注:functional继承,'functional'一词就不强行翻译了,个人理解为——函数式)

JavaScript 支持原型链继承,即对象从其他对象继承而来。class运算符也已经被添加到ES6的语言中。然而,它相比functional inheritance明显更复杂。大多数Node大师更喜欢后者(functional inheritance)的简单性。它由一个简单的函数工厂模式实现,不需要使用prototypenewthis。 当你更新原型(这会导致所有实例也跟着改变)时,并没有隐式的影响,因为在functional inheritance中,每个(实例化后的)对象使用它自己的方法副本。

请观察思考Express,Mocha,Connect,Superagent和几十个其他Node模块背后的多产天才TJ Holowaychuk的代码。在Express环境使用functional inheritance:(完整源代码):

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?

当我只看app.use()我是不知道dexter正在做什么的。 如果是一个不同的更有意义的名称——

const logger = require('morgan')
// ...
app.use(logger('dev')) // Aha!

同样,文件名必须正确反映代码内部的目的是什么。 如果你看看Node的lib文件夹 (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)标记,就像代码中的方法和变量一样。 这有助于警告开发人员这是一个内部接口,如果你使用了这个变量,导致它被重构或甚至删除,那么不要抱怨,这是你自找的。

Consider NOT Using JavaScript

啊哈?你确定看准确吗?这是什么鬼?没错。 就是这样的。 即使使用ES6和ES2016/ES7增加的两个功能,JavaScript仍然有它的诡异之处。 除了JavaScript,还有其他可以用的选择,你或你的团队可以从中受益,仅需稍微设置一下。根据专业水平和应用程序的性质,你可能会从像 TypeScriptFlow 这样的强语言。 另一方面,还有完全functional形式的脚本语言,像 ElmClojureScriptCoffeeScript 是另一个伟大的并且经过实战测试的可选语言。 你也可以参考 Dart 2.0

当你需要的只是一些宏操作(宏可以使你准确建立你想要语句),而不是需要一整套新语言,可以考虑 Sweet.js 它会做到这一点——允许你编写生成代码的代码。

如果进行非JavaScript的路由,请仍然包括你编译的代码,因为一些开发人员可能不太了解你的语言以正确构建它。 例如,VS Code是最大的TypeScript项目之一,也许随Angular 2之后,Code运用TypeScript使用types来对Node的核心模块打补丁。在VS code的repo vscode/src/vs/base/node/ 中(链接),你可以看到像crypto,process等熟悉的模块名称。不过有ts那样的后缀。还有其他ts文件在repo中。 但是,他们还包括带有原生JavaScript代码的vscode / build

了解Express中间件

Express是一个伟大而非常成熟的框架。它的光辉来自于允许无数其他模块对其行为进行配置。因此,你需要知道最常用的中间件,你需要知道如何使用它。所以何不利用 my Express cheat sheet。其中我有主要的中间件模块列出。 例如,npm i compression -S 将通过压缩响应来降低下载速度。 logger('tiny')logger('common')分别提供较少的(dev)或更多(prod)日志。

Scale up

Node在异步处理上是很杰出的,这归功于它的非阻塞I/O以及它一直以来简洁的异步过程的代码。这带来了提前开启放大的机会,也许甚至就是第一行代码开始。有一个核心的cluster模块,它将允许您垂直缩放,并且没有太多的问题。然而,更好的方法是使用工具,如pm2 或者 StrongLoop’s cluster control

例如,这是如何开始使用pm2:

npm i -g pm2

然后可以启动同一服务器的四个实例:

pm2 start server.js -i 4

对于Docker,pm2 version 2+ 有pm2-docker。所以你的Dockerfile可以看起来像这样:

# ...

RUN npm install pm2 -g

CMD ["pm2-docker", "app.js"]

官方Alpine Linux pm2镜像在 Docker Hub中。

缓存请求

这是一个开发的最佳实践,使得你可以从你的Node实例之外得到更多的关联(通过pm2,你可以得到的不止一个,见上)。需要使用的方法是让Node服务器做类似请求,处理数据和执行业务逻辑的应用程序,并将流量卸载到另一个Web服务器(如Apache httpd或Nginx)的静态文件。 你可能又要使用Docker进行设置:

FROM nginx

COPY nginx.conf /etc/nginx/nginx.conf

我喜欢使用Docker组合来制作多容器(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

总结

在这个软件开源的时代,没有借口不从开放的可信的和被测试过的代码中学习。你无需在核心圈便可以触及。学习永远不会停止,通过实践带来的失败和成功,我相信很快我们将有不同的最佳实用技巧。 这是肯定的。

最后,我想谈一谈软件是如何吞噬着这个世界,而世界是如何正在被JavaScript吞噬的。。。有很多伟大的东西,比如每年发布的标准,大量的npm模块,工具和参考等等。。。然而对此最终我表示谨慎。

我看到越来越多的人在追逐下一个新的框架或语言。 它是闪光物体综合征。 他们每个星期都学习一个新的库,每个月学习一个新的框架。 他们强制自己每周刷Twitter,Reddit,Hacker News和JS。 强制性的行为导致在JavaScript领域中的拖延。却没有一条GitHub记录。

学习新东西是好的,但不要误以为它就是实际建立出东西。 实际上是你建立的东西至关重要并劳有所得。 停止过多的工程理论。 你不是建立下一个Facebook。 Promises vs. generator vs. async await对我来说算不上是个问题,因为此时在讨论中已有人对于单个线程做出回复,我已经写了我的回调函数(并且使用CoffeeScript做到比普通ES5 / 6/7快2倍的速度! )。

最后,最佳的实践是使用最佳实践,掌握基础知识是最重要的。 阅读源代码,在代码中尝试新东西,最重要的还是自己输出成吨的代码。 现在,就在此时,有意义的十停止阅读和去发布代码!

以防本篇文章不够,这里是一些更好的节点Node阅读材料:

译者Olmsted尚未开通打赏功能

相关文章