Home > Article > Web Front-end > 10 Tips to Become a Better Node Developer in 2017
Below I will list 10 suggestions that can help you become a better Node developer in 2017. Some of this advice is something I've learned in my daily practice, and some is something I've learned from the people who have written the most popular Node and npm modules. Here’s what we’ll cover:
1.Avoid Complexity — Split your code blocks into the smallest possible size, as small as possible.
2.Use Asynchronous Programming — Avoid synchronous code like the plague.
3.Avoid require blocking — Put all your require statements at the top of the file, because require is synchronous and will block the code from running.
4.Understand the require cache — If you know it, you can take advantage of it, otherwise it may cause bugs.
5.Always check for errors — Errors are not football, never throw an error or skip error checking at any time.
6.Only use try...catch in synchronous code - try...catch has no effect in asynchronous code. The V8 engine cannot be optimized for try...catch.
7.Return callbacks or use if ... else — Return a callback just to ensure that execution does not continue.
8.Listen to error events - Almost all Node classes/objects have event emitters (observer mode) and will broadcast error events, make sure you listen to them.
9.Know your npm — Use -S or -D to install modules instead of --save or --save-dev`.
10.Use the exact version number in package.json: npm will automatically use the default version number when using -S to install the module. You need to manually modify it to lock the version number. . Unless it's an open source module, don't trust SemVer (Semantic Versioning Standard) in your project.
11. Bonus points — using different dependencies. Put the things that the project needs during the development phase in devDependencies, remember to use npm i --production. The more redundant dependencies you have, the greater the risk of problems.
Okay, let us understand each of the above points one by one.
##avoid complexity
var module = require('module') module.wrapper[0] += '"use strict";' Object.freeze(module.wrap)So why should we What about avoiding complexity? A famous phrase originating from the U.S. Navy: KEEP IT SIMPLE STUPID (or "Keep it simple, stupid"). That's why. It turns out that the human brain can only hold five to seven items in its working memory at any one time. Modularize your code into smaller pieces, and you and other developers will understand it better. You can also test it better. As an example below,
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() })or
const auth = require('./middleware/auth.js') const db = require('./middleware/db.js')(db) app.use(auth, db)I believe most people will like the second example, especially since you can understand its function just by looking at the name. Back in the day, when you were writing code, you probably thought you knew how it worked. Or even if you want to show how clever you are by connecting several functions together on the same line. However, you are writing a stupid piece of code. If you think very complicatedly to write this code, it will be difficult to understand it when you look at this code in the future. Keep your code simple, especially in Node's asynchronous code. Of course there will be a left-pad event, but in fact it only affects projects that depend on the left-pad module and a replacement was released 11 minutes later. The benefits of code minimization outweigh its disadvantages. npm has changed its release policy and any serious projects should use cached or private repositories (as a temporary solution).
Using asynchronous programming
let data = fs.readFileSync('./acconts.json') db.collection('accounts').insert(data, (results))=>{ fs.writeFileSync('./accountIDs.json', results, ()=>{process.exit(1)}) })But when you create a web application , the following writing would be better:
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()}) }) }) })The difference lies in whether you need to write a concurrent (usually long-term running) or non-concurrent (short-term running) system. As a rule of thumb, always use asynchronous code in Node.
Avoid require blocking
`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会生成一个包含依赖的具体版本的文件。