title | categories | tags | date | keywords | ||
---|---|---|---|---|---|---|
【Node】16年,新 Node 项目注意点 |
技术 |
|
2016-01-10 23:56:29 -0800 |
Nodejs, 前端, ES6 |
16年开发 node 项目,会有什么不一样吗?! 当然随着语言,工具,运行环境,开发方式的不断变化,你的 node 项目当然也需要不断的与时俱进,那么我们就依次来看看需要注意哪些问题吧。
目录
- 现在开始使用 ES2015
- 异步函数支持回调惯例和Promise新写法
- 异步模式
- 错误处理
- 使用标准的 JavaScript 代码风格
- Web 应用开发的十二条军规
- 始终用 npm init 开始新项目
- 文件名始终小写
- 智能的 .npmrc 和正确的版本管理做法
- 及时更新依赖
- 选择合适的数据库
- 监控你的应用程序
- 使用构建系统
- NPM 生命周期钩子
- 管好垃圾回收
- 使用长期支持的 Node.js 版本
- 使用语义化的版本号
- 持续学习和跟上潮流
箭头函数 模板字符串 rest参数,扩展运算符(spread),函数默认值 变量的解构赋值 generator 和 promises maps,sets 和 symbols
这些新语法大部分都被 Node.js V4 支持(参考)[https://nodejs.org/en/docs/es6/] 在服务器端的推荐使用新的 node 解释器支持的语法,或者可以用babel作为compile层(具体做法参考脚手架)
// 从 express's req.query 解构 特定参数 ?page=2&size=10&word=测试
let {page, size, word} = req.query;
过去,当Promise没有成为Node默认搭载的语法时,推荐模块通过导出 error-first callback 的接口形式。但是现在通常需要支持两种形式:
const fs = require('fs')
function readPackage(callback=noop) {
return new Promise((resolve, reject)=>{
fs.readFile('./package.json', (err, data)=>{
if(err) {
reject(err)
return callback(err)
}
resolve(data)
return callback(null, data)
})
})
}
过去很长时间,在 node 中一般有两种方式来管理异步流:callback回调和 streams 流 前者可以用辅助我们异步操作的 async 类库 后者可以用through, bl or highland 这些类库 但是随着 es6的 generator和promise的到来,甚至es7的 await/async 内建关键字的到来,情况变了。 详细请看 异步JavaScript的演进
完善合理的错误处理让你的服务更加强健。知道何时crash,然后是仅仅catch后忽略,还是记下调用栈打入log后重试,甚至是需要重启? 我们通常需要区别对待 programmer error, operational errors: 前者直接重启(事实上在开发阶段就该发现,并且线上通过 logger 定位),因为程序员写的bug,如果不及时重启会导致应用的状态难以推演,从而发生更多更大的问题 而后者,通常不是bug,而是没有考虑全的case。如外部请求超时了,外部依赖的数据库连不上了,甚至所在运行的机器磁盘写满了,要访问写入的文件暂时不存在了。这些case一般需要在程序里加上特定的fallback/polyfill 来处理。如对于超时的重试几次,对于不存在的文件先试着创建新文件,对于总是塞满磁盘的log,通过logstash和logrotate去处理。
error-first 约定的callback,始终记得在函数开始检查第一个err是否存在,然后进行合适的处理(当然也可以通过 next(e) 传入到调用栈的最后统一处理)
始终记得在 promise 调用链的最后加上 catch 来处理异常
过去我们使用 jslint, jshint, jscs 来作为我们的代码风格检查工具,但是随着 es6 的流行,还有一些新的习惯的养成,我们推荐使用 eslint 工具,同时配合 eslint-plugin-standard 插件
{
"plugins": [
"standard"
],
}
来自于 Rails 社区的血泪经验,但是大部分也是适用于我们Node项目 (一些实践可能在新的docker部署下会有小调整)
- 一份基准代码Codebase,多份部署deploy
- 显示声明和隔离依赖
- 在配置放在环境中
- 把外部后端服务当做附加资源
- 严格分离构建和运行环境
- 以一个或多个无状态进程运行应用
- 通过端口绑定(Port binding)来提供服务
- 通过进程模型进行扩展
- 快速启动和优雅终止可最大化健壮性
- 尽可能的保持开发,预发布,线上环境相同
- 把日志当作事件流
- 后台管理任务当作一次性进程运行
通过 npm init 来初始化你的node项目,通过promt 确定你的项目名称,开发者信息等(当然你可以通过 --yes 旗标来跳过) Tip: 主要你应该总是显示指名你的node engines 版本(node -v),确保你的开发环境,测试环境和线上环境是用同一版本的 node.
{
"engines": {
"node": "4.2.1"
}
}
因为在 OSX 和 Windows 系统中,MyClass.js 和 myclass.js 没有任何区别,Linux 则会区分。所以为了你写的代码在不同操作系统是可移植的(在使用 require 来引入模块确保语句是一致明确的),所以始终保持小写 - my-class.js
默认上, npm 在安装新的依赖的modules,默认不会加入到package.json中。同时,modules的版本号不是严格锁死的(^尖角号来确保大版本保持一致)这样会造成一些问题,如在发布时才发现没有把依赖写入到package.json中,造成线上缺少必要的模块,线上部署发现用的不是相同的modules,导致莫名其妙的问题和大量的depricated warning警告。
所以安装新依赖推荐这样的写法:npm install foobar --save --save-exact
或者写入.npmrc 这样下次 npm install 就不会犯错啦
$ npm config set save=true
$ npm config set save-exact=true
$ cat ~/.npmrc
当然啦,如果如果希望更灵活的依赖控制,可以通过 npm shrinkwrap
命令生成 npm-shrinkwrap.json
加入到版本库中,这样在build环境构建也能保证版本统一。
上面的版本锁死让你面对依赖模块的时候更加从容,但是要记得保持定期更新依赖,从而获得修复bug和性能优化功能完善的更新。可以每周利用 npm outdated
或 ncu 工具包
大部分新的noder,在选择数据库,喜欢选择Mongodb。它的确很不错,但是 Mongodb 不是唯一的选择、 你应该根据你的应用场景来选择:
- 你的数据是否结构化的
- 你的数据操作是否要支持事务
- 你数据是否需要持久化
从而选择不同的数据库:如 PostgreSQL, Redis, LevelDB 等等
你要对你的线上应用的运行状况了如指掌(CPU,Memory,日志等),对一些突发情况需要及时获得通知。 很多开源项目和SaaS产品都提供完善强大的监控服务,如Zabbix, Collectd, ElasticSearch 和 Logstash. 甚至结合Cabot给微信公众号发消息提醒等等
现在的JavaScript的工具链有大量的选择: Grunt, Gulp, Webpack等。譬如在团队里,我们选择 Webpack 来辅助前端开发,gulp用来处理大量其他的自动化任务(你的shell脚本也可以通过gulp-shell集成进来)。当然我们也推荐使用 vanilla build (尤其你可以结合 npm lifecycle hooks 完成很多事)
提供了很好的钩子来使得一些task实现的很优雅,我们的脚手架大量使用了这样的技巧
"postinstall": "bower install && grunt build",
"postinstall": "if $BUILD_ASSETS; then npm run build-assets; fi",
"build-assets": "bower install && grunt build"
# 如果脚本变复杂可以单独文件:
"postinstall": "scripts/postinstall.sh” (sh 脚本中会自动可以访问到 ./node_modules/.bin 中的命令,因为该路径被加入到 $PATH)
v8默认使用 lazy 和 贪婪的 GC. 有时候等到1.5GB 自由才去回收未被使用的内存 (所以有时候内存涨不是因为泄露还是node’s usual lazy behavior)
所以你不想自己的node应用经常把服务器的内存占满(或者你不得不调整,因为你的机器可用内存没那么多),试着使用下面的命令/proc 文件来启动 node 服务(推荐写在 .pm2config 中,正如脚手架推荐的)
web: node --optimize_for_size --max_old_space_size=920 --gc_interval=100 server.js
如果你需要在不同项目中工作,并且不同项目用的node版本还不一样,可以使用 node version manager(nvm)
通过三段版本数来确保把兼容性声明好。 major.minor.patch 这样的格式,不同级别的升级对API更新的要求也是不一样的。可以通过semantic-release 来让版本升级更加自动化
JavaScript 和 Node.js 社区异常活跃,的确是件好事。每周都有新的工具新的理念的加入,让我们始终保持热情和技术的提升(警惕自己变成跟风狗,要取色和了解每个新东西背后的不变的本质),不要待在自己的蜜罐中,要动手做试验和学习。以下资料:
- Node.js Weekly Newsletter
- Microservice Weekly Newsletter
- Changelog Weekly - for Open-Source news