아래에는 2017년에 더 나은 Node 개발자가 되는 데 도움이 될 수 있는 10가지 제안 사항이 나와 있습니다. 이 조언 중 일부는 제가 일상 업무에서 배운 내용이고, 일부는 가장 인기 있는 Node 및 npm 모듈을 작성한 사람들로부터 배운 것입니다. 우리가 다룰 내용은 다음과 같습니다.
1. 복잡함 방지 — 코드 블록을 가능한 한 가장 작은 크기로 분할하세요.
2. 비동기 프로그래밍 사용 — 전염병과 같은 동기 코드를 피하세요.
3.require 차단 방지 — require는 동기식이며 코드 실행을 차단하므로 모든 require 문을 파일 상단에 배치하세요.
4.필요 캐시 이해 — 알고 있으면 활용할 수 있고, 그렇지 않으면 버그가 발생할 수 있습니다.
5.항상 오류를 확인하세요 — 오류는 축구가 아니며, 언제든지 오류를 던지거나 오류 검사를 건너뛰지 않습니다.
6.동기 코드에서는 try...catch만 사용하세요 — try...catch는 비동기 코드에서는 효과가 없습니다. V8 엔진은 try...catch에 최적화될 수 없습니다.
7.콜백을 반환하거나 if ... else 사용 — 실행이 계속되지 않도록 콜백을 반환합니다.
8.오류 이벤트 듣기 — 거의 모든 노드 클래스/객체에는 이벤트 방출기(관찰자 모드)가 있으며 오류 이벤트를 브로드캐스팅하므로 꼭 들어보세요.
9.npm을 알아보세요 — 모듈을 설치하려면 --save 또는 --save-dev` 대신 -S 또는 -D를 사용하세요.
10.package.json에서 정확한 버전 번호 사용: npm은 -S를 사용하여 모듈을 설치할 때 자동으로 기본 버전 번호를 사용합니다. 버전 번호. 오픈 소스 모듈이 아니라면 프로젝트에서 SemVer(Semantic Versioning Standard)를 신뢰하지 마십시오.
11. 보너스 포인트 — 다양한 종속성을 사용합니다. 개발 단계에서 프로젝트에 필요한 사항을 devDependency에 넣고 npm i --production을 사용하는 것을 잊지 마세요. 중복된 종속성이 많을수록 문제가 발생할 위험이 커집니다.
자, 위의 내용을 하나씩 이해해 보도록 하겠습니다.
복잡함을 피하세요
한 번 살펴보겠습니다. npm use-strict의 작성자인 Isaac Z. Schlueter가 작성한 일부 모듈은 Javascript에서 엄격 모드를 강제로 사용하는 데 사용됩니다. 이 모듈에는
var module = require('module') module.wrapper[0] += '"use strict";' Object.freeze(module.wrap)
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)
비동기 프로그래밍 사용
let data = fs.readFileSync('./acconts.json') db.collection('accounts').insert(data, (results))=>{ fs.writeFileSync('./accountIDs.json', results, ()=>{process.exit(1)}) })
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()}) }) }) })
요구 차단 방지
`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会生成一个包含依赖的具体版本的文件。