首页 > web前端 > js教程 > 正文

10 个技巧,让你在2017 年成为更好的 Node 开发者

阿神
发布: 2017-01-24 11:40:35
原创
1147 人浏览过

下面我将列出10条建议,这些建议可以帮助你在2017年成为一个更好的Node开发者。其中一些建议是我在日常实践中所学到的,另一些是从那些写了最流行的Node和npm模块的人们身上学到的。 下面是我们将要介绍的内容:

1.避免复杂性 — 尽可能将你的代码块拆到最小,要小到极致。

2.使用异步编程 — 像躲避瘟疫般避免使用同步代码。

3.避免require阻塞 — 把你所有的require声明都放在文件的顶部,因为require是同步的,会阻塞代码运行。

4.了解require缓存 — 了解它则可以利用它,否则它可能会带来bug。

5.始终检查错误 — 错误不是足球,任何时候都不要抛出错误或者跳过错误检查。

6.只在同步代码中使用try…catch — 在异步代码中try...catch是没有作用的。V8引擎针对try...catch无法进行优化。

7.返回callbacks或者使用if … else — 返回一个callback只是为了确保不继续执行。

8.监听错误事件 — 几乎所有的Node的类/对象都有event emitter(观察者模式)并且会广播error事件,确保你监听了它们。

9.了解你的npm — 使用-S或者-D来安装模块来代替--save或者--save-dev`。

10.在package.json中使用精确的版本号: npm在使用-S来安装模块时会自动使用默认的版本号,你需要手动修改去锁定版本号。除非是开源模块,否者不要相信你的项目中的SemVer(语义化版本标准)。

11.加分 — 使用不同的依赖。把项目在开发阶段需要的东西放在 devDependencies 中,记得使用 npm i --production。多余的依赖越多,出现问题的风险就越大。

好的,接下来让我们一个个单独地去了解上面的每一点。


避免复杂性

让我看一眼npm的创造者Isaac Z. Schlueter写的一些模块,例如,use-strict,这个模块是用来在Javascript中强制使用严格模式,这个模块仅仅只有三行代码:

var module = require('module')
module.wrapper[0] += '"use strict";'
Object.freeze(module.wrap)
登录后复制

所以我们为什么要避免复杂性呢? 一个起源于美国海军的著名短语:KEEP IT SIMPLE STUPID(或者是“Keep it simple, stupid”)。这就是原因。事实说明,人类大脑在任何一个时间只能在其工作记忆中保持五到七个项目。

把你的代码模块化成一个更加小的部分,你和其他的开发者会更加好的理解它。你也可以更加好的去测试它。如下例子,

app.use(function(req, res, next) {
  if (req.session.admin === true) return next()
  else return next(new Error('Not authorized'))
}, function(req, res, next) {
  req.db = db
  next()
})
登录后复制

或者是

const auth = require('./middleware/auth.js')
const db = require('./middleware/db.js')(db)

app.use(auth, db)
登录后复制

我相信大多数人都会喜欢第二个例子,特别是光看名字就能了解其作用。当日,在你编写代码的时候,你可能认为你知道代码是如何运行的。甚至你想要展示你把几个功能连接在一起写在同一行中是多么的机智。但是,这样你是写了一段愚蠢的代码。如果你思考的很复杂去写这代码,那么今后你再去看这段代码将会很难去理解。保证你的代码简单,特别是在Node的异步代码中。

当然也会有left-pad 事件,但是其实它只是影响了依赖于left-pad模块的项目而且11分钟后就发布了替代品。代码的最小化带来的好处超过了它的缺点。npm已经改变了发布策略,任何重要的项目都应该使用缓存或私有的源(作为临时解决方案)。


使用异步编程

在Node中同步代码只要很小的一部分。这些代码大多数都是用于命令行工具或者其他与web应用无关的脚本。Node开发者大多数都是编写web应用,因此使用异步代码可以避免阻塞现场。

例如,当你在编写一个数据库的脚本或者是一个不需要控制并行的任务时,下面这种写法可能是可以的:

let data = fs.readFileSync('./acconts.json')
db.collection('accounts').insert(data, (results))=>{
  fs.writeFileSync('./accountIDs.json', results, ()=>{process.exit(1)})
})
登录后复制

但是当你创建一个web应用时,下面这个写法会更好:

app.use('/seed/:name', (req, res) => {
  let data = fs.readFile(`./${req.params.name}.json`, ()=>{
    db.collection(req.params.name).insert(data, (results))=>{
      fs.writeFile(`./${req.params.name}IDs.json`, results, ()={res.status(201).send()})
    })
  })
})
登录后复制

这个区别在于你是否需要编写一个并发(通常是长期运行)或者非并发(短期运行)的系统。根据经验来说,总是要在Node中使用异步代码。


避免require阻塞

Node有一个使用了CommonJS模块格式的简单的模块加载系统。它是基于require函数,require函数可以很方便的在不同的文件中引入模块。和AMD/requirejs不同,Node/CommonJS的模块加载时同步的。require的工作方式是:引入一个模块或者一个文件export的内容:

`const react = require('react')`
登录后复制

但是大多数的开发者并不知道require是会被缓存的。因此,只要解析的文件名(resolved filename)没有剧烈的变化(比如npm模块不存在的情况),模块的代码只会被执行并存入变量中一次(在当前进程中)。这是一个很好的优化。当然,即使有了缓存,你最好还是把你的require声明写在开头。下面这段代码,它在路由中真正使用到了axios模块的时候才加载。当请求发送的时候/connect会因为需要加载模块所以会变得慢。

app.post('/connect', (req, res) => {
  const axios = require('axios')
  axios.post('/api/authorize', req.body.auth)
    .then((response)=>res.send(response))})
登录后复制

一个更好,性能更优的方式是在服务定义之前就引入模块而不是在路由中:

const axios = require('axios')
const express = require('express')
app = express()
app.post('/connect', (req, res) => {
  axios.post('/api/authorize', req.body.auth)
    .then((response)=>res.send(response))
})
登录后复制


知道require会被缓存

我在上面一节已经提到了require会被缓存,但是有趣的是我们在module.exports之外也会有代码。举例来说:

console.log('I will not be cached and only run once, the first time')

module.exports = () => {
  console.log('I will be cached and will run every time this module is invoked')
}
登录后复制

从中我们了解到有一些代码只会运行一次,你可以使用这个特性来优化你的代码。


始终检查错误

Node不是Java。在Java中,你可以抛出错误,因为如果发生了错误那么你会希望应用不在继续执行。在Java中,你可以在外层仅仅使用一个简单的try...catch就可以处理多个错误。

但是在Node中并不是这样的。自从Node使用了事件循环和异步执行后,任何的错误发生时都会与错误处理器(例如try...catch)的上下文分离,下面这样做在Node中是没有用的:

try {
  request.get('/accounts', (error, response)=>{
    data = JSON.parse(response)
  })
} catch(error) {
  // Will NOT be called
  console.error(error)
}
登录后复制

但是try...catch在同步代码中是可以被用的。前面的代码片段可以被更好的重构为:

request.get('/accounts', (error, response)=>{
  try {
    data = JSON.parse(response)
  } catch(error) {
    // Will be called
    console.error(error)
  }
})
登录后复制

如果我们无法将request的返回内容包裹在try...catch中,那么我们将没有办法去处理请求的错误。Node的开发者通过在返回的参数里面加上error来解决了这个问题。因此,我们需要在每一个回调中手动去处理错误。你可以去检查这些错误(判断error不是null),然后展示错误信息给用户或者展示在客户端上并且记录它, 或者你可以通过调用 callback ,给它传 error 参数,将错误传回给上一级调用栈(如果你在调用栈之上有另一个回调函数)。

request.get('/accounts', (error, response)=>{
  if (error) return console.error(error)
  try {
    data = JSON.parse(response)
  } catch(error) {
    console.error(error)
  }
})
登录后复制

一个小技巧是你可以使用okay库。你可以像下面的例子一样使用它去避免在回调地狱中手动去检查错误(你好, 回调地狱).

var ok = require('okay')

request.get('/accounts', ok(console.error, (response)=>{
  try {
    data = JSON.parse(response)
  } catch(error) {
    console.error(error)
  }
}))
登录后复制


返回回调或者使用if … else

Node是并行的。但是如果你不够细心也会因为这个特性产生bug。 为了安全起见,应该要使用return来终止代码的继续执行:

let error = true
if (error) return callback(error)
console.log('I will never run - good.')
登录后复制

这样可以避免一些因为代码逻辑的处理不当导致一些不应该执行的内容(或者错误)被执行。

let error = true
if (error) callback(error)
console.log('I will run. Not good!')
登录后复制

请确保使用return去阻止代码的继续执行。


监听 error 事件

Node中几乎所有的类/对象都有事件分发器(观察者模式)并且会广播 error 事件。 这是一个很好的特性,可以使开发者在这些讨厌的错误造成巨大后果之前捕捉到它们。

养成一个通过.on()来创建error事件监听的好习惯:

var req = http.request(options, (res) => {
  if (('' + res.statusCode).match(/^2\d\d$/)) {
    // Success, process response
  } else if (('' + res.statusCode).match(/^5\d\d$/))
    // Server error, not the same as req error. Req was ok.
  }
})

req.on('error', (error) => {
  // Can't even make a request: general error, e.g. ECONNRESET, ECONNREFUSED, HPE_INVALID_VERSION
  console.log(error)
})
登录后复制


了解你的npm

很多的Node和前端的开发者知道在安装模块的时候使用--save会在安装模块的同时,会在package.json保存一条含有模块版本信息的条目。当然,还有--save-dev可以用于安装devDependencies(在生成环境中不需要的模块)。但是你知道用-S和-D是否可以代替--save 和--save-dev么?答案是可以的。

当你安装模块的时候,你需要删除-S和-D自动为你模块的版本号添加的^标签。否者当你使用npm install(或者npm i)安装模块的时候,就会自动拉取最新的镜像(版本号的第二位数字)。例如v6.1.0就是v6.2.0的一个镜像分支。

npm团队推荐使用semver,但是你最好不要这样。npm团队认为开源开发者会遵守semver所以他们在npm安装时自动加上了^。没有人可以去保证,所以最好是锁定你的版本号。更好的办法是使用shrinkwrap:npm shrinkwrap会生成一个包含依赖的具体版本的文件。


相关标签:
来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板