最佳Node实践之实用十式:受我们的客邀作者,Node大师Azat Mardan的启发。 SitePoint 的客座发帖旨在带给大家web社区里杰出的作者和宣讲者的优秀内容。
在我之前的文章: 10 Tips to Become a Better Node Developer in 2017中,介绍了10个Node.js的技巧,现在你已经可以应用到自己的代码中。而这篇文章与此前的文章一脉相承,10个进一步的最佳实践技巧帮助你的Node技能更上一层。以下是我们将涉及到的内容:
npm run build
, start
和 test
。npm scripts 就像Node开发人员接触一个新的项目的时去获取真相的唯一途径。
process.env.NODE_ENV
设置为 development
或 production
来使用。 一些框架将也会使用这个变量,所以按游戏规则进行吧。
setImmediate()
不是立即的,而 nextTick()
不是指下一个。 使用 setImmediate()
或 setTimeout()
将CPU密集型任务卸放到下一个事件循环周期。
var
/ const
/ let
、分号、 class
和其它方面的争议上。
npm i compression-S
和完整的日志记录 — 不多也不少地依赖于环境. npm i morgan-S
那么让我们一个个分开看看他们吧。走起!
创建并使用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"`
即使在项目的早期阶段也要使用环境变量,以确保没有泄漏敏感信息,从一开始就得正确地构建代码。此外,一些库和框架(我确定Express就是这么做的)将使用如NODE_ENV信息,来修正它们的行为。 将其设置为 production
。 设置你的 MONGO_URI
和 API_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提供的图示,他从事事件循环方面的工作,清楚知道事件循环如何工作!
(译注:functional继承,'functional'一词就不强行翻译了,个人理解为——函数式)
JavaScript 支持原型链继承,即对象从其他对象继承而来。 class
运算符也已经被添加到ES6的语言中。然而,它相比functional inheritance明显更复杂。大多数Node大师更喜欢后者(functional inheritance)的简单性。它由一个简单的函数工厂模式实现,不需要使用 prototype
, new
或 this
。 当你更新原型(这会导致所有实例也跟着改变)时,并没有隐式的影响,因为在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
)标记,就像代码中的方法和变量一样。 这有助于警告开发人员这是一个内部接口,如果你使用了这个变量,导致它被重构或甚至删除,那么不要抱怨,这是你自找的。
啊哈?你确定看准确吗?这是什么鬼?没错。 就是这样的。 即使使用ES6和ES2016/ES7增加的两个功能,JavaScript仍然有它的诡异之处。 除了JavaScript,还有其他可以用的选择,你或你的团队可以从中受益,仅需稍微设置一下。根据专业水平和应用程序的性质,你可能会从像 TypeScript 或 Flow 这样的强语言。 另一方面,还有完全functional形式的脚本语言,像 Elm 或 ClojureScript。 CoffeeScript 是另一个伟大的并且经过实战测试的可选语言。 你也可以参考 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是一个伟大而非常成熟的框架。它的光辉来自于允许无数其他模块对其行为进行配置。因此,你需要知道最常用的中间件,你需要知道如何使用它。所以何不利用 my Express cheat sheet。其中我有主要的中间件模块列出。 例如, npm i compression-S
将通过压缩响应来降低下载速度。 logger('tiny')
或 logger('common')
分别提供较少的(dev)或更多(prod)日志。
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阅读材料:
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
往期精选文章 |
---|
使用虚拟dom和JavaScript构建完全响应式的UI框架 |
扩展 Vue 组件 |
使用Three.js制作酷炫无比的无穷隧道特效 |
一个治愈JavaScript疲劳的学习计划 |
全栈工程师技能大全 |
WEB前端性能优化常见方法 |
一小时内搭建一个全栈Web应用框架 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
让你分分钟理解 JavaScript 闭包 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。