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)
})
专业的网站建设公司,深正互联,如您有网站营销需求,请您关注我们,或者致电13828884598
深圳 · 龙岗 · 大运软件小镇22栋201
电话:400 182 8580
邮箱:szhulian@qq.com