轉(zhuǎn)帖|行業(yè)資訊|編輯:龔雪|2017-03-01 11:34:33.000|閱讀 190 次
概述:在這篇中,筆者會(huì)列出 2017 年成為更好 Node 開發(fā)者的技巧的大綱。這些技巧一部分是在具體實(shí)踐中總結(jié)得出的,一部分借鑒了 Node 和 npm 優(yōu)秀模塊開發(fā)者的經(jīng)驗(yàn)。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
注意: 這篇文章之前的標(biāo)題是 “來自平臺(tái)大神的最佳實(shí)踐分享”。文章內(nèi)容是經(jīng)過實(shí)際的測試和試驗(yàn)寫出,但并非 2017 年最新技巧。 雖然,Node 大神分享的最佳經(jīng)典實(shí)踐在 2017 年,2018 年甚至 2019 年都會(huì)受用,但一些尖端功能,像 async/await, promises 并沒有涵蓋在內(nèi)。因?yàn)檫@些新特性不在 Node 核心代碼中,也不如 npm 和 Express 流行。文章第二部分,我將介紹此文章的性質(zhì)。
我開始作為全職Node開發(fā)是在2012年,當(dāng)我加入了Storify公司。 從那時(shí)起,我從沒有回頭或者覺得掛念Python,Ruby,Java和PHP——這些貫穿我之前十年web開發(fā)工作的語言。
Storify對(duì)我來說是一份有趣的工作,因?yàn)椴幌翊蠖鄶?shù)公司, Storify所有的項(xiàng)目運(yùn)行(也許今后依舊)都是使用Javascript。你看,大多數(shù)公司,尤其是大公司如貝寶(PayPal)、沃爾瑪(Walmart)、或第一資本(Capital One),只是使用Node作為他們堆棧的某部分。通常他們使用它作為API途徑或一個(gè)業(yè)務(wù)流程層。這也是不錯(cuò)的,但是對(duì)于一個(gè)軟件工程師來說,沒有什么比完全沉浸在一個(gè)Node環(huán)境中更棒的了。
我這篇博客中,我會(huì)列出 2017 年成為更好 Node 開發(fā)者的技巧的大綱。這些技巧一部分是我在具體實(shí)踐中總結(jié)得出的,一部分借鑒了 Node 和 npm 優(yōu)秀模塊開發(fā)者的經(jīng)驗(yàn)。主要內(nèi)容如下:
下面,讓我們對(duì)每項(xiàng)內(nèi)容都進(jìn)行一下了解吧~
避免復(fù)雜性
npm 的作者 Isaac Z. Schlueter 寫了一些模板,我們可以看下。例如, 用 在模塊中強(qiáng)制實(shí)施 JavaScript 嚴(yán)格模式,只需要三行代碼:
var module = require('module') module.wrapper[0] += '"use strict";' Object.freeze(module.wrap)
那么,為什么要避免復(fù)雜性呢? 美國海軍傳說中,有一句很著名的話:化繁為簡,返璞歸真(或理解為“編碼的方式簡單點(diǎn),傻蛋!”)事實(shí)證明,人類大腦一次只能記住 5 項(xiàng)至 7項(xiàng)的內(nèi)容,這就是此處要求簡單的原因。
縮小代碼模塊,有利于你和開發(fā)者更好地理解代碼,你也能更好地進(jìn)行測試。參考以下示例:
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)
我相信大多數(shù)的讀者都更喜歡第二個(gè)示例,尤其是在名稱可以自解釋時(shí)。當(dāng)然,在你寫代碼時(shí),你能理解它的運(yùn)行方式。或許,你還會(huì)為能將如此多的方法用在一行代碼中而沾沾自喜。但是,代碼不會(huì)說話啊。如果你把代碼寫的復(fù)雜而嚴(yán)謹(jǐn),當(dāng)你時(shí)隔六個(gè)月再來看,或者某天你睡迷糊了或喝醉了來看,理解起它們來會(huì)非常困難,更不要說根本不了解其算法和復(fù)雜性的同事了。保持簡單,這一準(zhǔn)則在使用 Node 異步方式的時(shí)候特別受用。
有一種 ,不過它只影響了依賴公共注冊(cè)表的項(xiàng)目,并在 11 分鐘之后重新發(fā)布。最小化所帶來的益處遠(yuǎn)大于其缺點(diǎn)。而且,npm 已經(jīng),任何重要的項(xiàng)目都應(yīng)該使用緩存或私有注冊(cè)中心(作為臨時(shí)解決方案)。
使用異步代碼
同步代碼確實(shí)在 Node 中有一個(gè)(低的)位置。 它主要用于編寫 CLI 命令或與 Web 應(yīng)用程序無關(guān)的其他腳本。Node 開發(fā)者主要構(gòu)建 Web 應(yīng)用程序,因此他們使用異步代碼,以避免阻塞線程。
例如,這可能是可行的,如果我們只是建立一個(gè)數(shù)據(jù)庫腳本,而不是一個(gè)系統(tǒng)來處理并行/并發(fā)任務(wù):
let data = fs.readFileSync('./acconts.json') db.collection('accounts').insert(data, (results))=>{ fs.writeFileSync('./accountIDs.json', results, ()=>) })
但當(dāng)建立一個(gè)Web應(yīng)用程序時(shí),這樣寫會(huì)更好:
app.use('/seed/:name', (req, res) => { let data = fs.readFile(`./$.json`, ()=>{ db.collection(req.params.name).insert(data, (results))=>{ fs.writeFile(`./$IDs.json`, results, ()=) }) }) })
區(qū)別就在于你是寫并發(fā)系統(tǒng)(長期運(yùn)行)還是非并發(fā)系統(tǒng)(短期運(yùn)行)。根據(jù)實(shí)踐經(jīng)驗(yàn),我們通常在 Node 中使用異步代碼。
避免阻塞請(qǐng)求
Node 有個(gè)簡單的模板加載系統(tǒng),它使用的是 CommonJS 模板規(guī)范。它內(nèi)置的 require 函數(shù)是將單獨(dú)存放的模板包含進(jìn)來的簡易方式。不像 AMD/requirejs,Node/CommonJS 的模板加載方式是同步的。require 的工作原理:導(dǎo)入在模板或文件中導(dǎo)出的內(nèi)容。
const react = require('react')
大多數(shù)開發(fā)者都不知道 require 有緩存。因此,只要解析的域名沒有變化(在 nmp 模板中是沒有的), 模塊中的代碼會(huì)只執(zhí)行一次并將結(jié)果保存在一個(gè)變量中(同一進(jìn)程內(nèi))。這是一個(gè)很不錯(cuò)的優(yōu)化。然而,即使在緩存的情況下,你也最好把 require 語句放在前面。 看看下面的代碼,在真正進(jìn)入路由的時(shí)候才加載 axios 模塊。/connect 路由出乎預(yù)料的慢,因?yàn)樗趯?dǎo)入模塊的時(shí)候才開始請(qǐng)求文件:
app.post('/connect', (req, res) => { const axios = require('axios') axios.post('/api/authorize', req.body.auth) .then((response)=>res.send(response)) })
更好更高效的方式是服務(wù)器啟動(dòng)后就加載模塊,而不是在路由中:
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 外部進(jìn)行編碼,比如:
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') }
知道有些代碼只運(yùn)行一次,你可以將這一特點(diǎn)作為優(yōu)勢。
時(shí)刻檢查錯(cuò)誤
Node 不是 Java,在 Java 中,你可以拋出錯(cuò)誤,因?yàn)榇蠖鄶?shù)時(shí)候,當(dāng)你發(fā)現(xiàn)錯(cuò)誤時(shí),你會(huì)中止應(yīng)用程序的運(yùn)行。并且,在 Java 中,你可以通過單獨(dú)的 try...catch 處理多個(gè)錯(cuò)誤。
但 Node 不是這樣。因?yàn)?Node 使用的是事件循環(huán)和異步執(zhí)行,在錯(cuò)誤發(fā)生時(shí),任何錯(cuò)誤都與錯(cuò)誤處理程序的上下文分離(比如 try...catch)。下述代碼在 Node 中無效:
try { request.get('/accounts', (error, response)=>{ data = JSON.parse(response) }) } catch(error) { // Will NOT be called console.error(error) }
但 try...catch 也可在 Node 同步代碼中使用。因此,這是重構(gòu)前面代碼段的更好的方式:
request.get('/accounts', (error, response)=>{ try { data = JSON.parse(response) } catch(error) { // Will be called console.error(error) } })
如果我們不能將 request 調(diào)用放在 try...catch 塊中,我們就不能處理來自 request 的錯(cuò)誤。Node 開發(fā)者采用提供包含 error 參數(shù)的回調(diào)來解決這個(gè)問題。這樣你需要在每個(gè)回調(diào)中手工處理錯(cuò)誤。你需要檢查 error(確保它不是 null),然后將相應(yīng)的錯(cuò)誤消息顯示給用戶或者客戶端,并記錄下來。也可以通過調(diào)用棧中的 callback 往回傳(如果你有回調(diào),而且調(diào)用棧上還有另一個(gè)函數(shù))。
request.get('/accounts', (error, response)=>{ if (error) return console.error(error) try { data = JSON.parse(response) } catch(error) { console.error(error) } })
你還可以使用 庫。 你可以使用以下代碼來避免在無數(shù)回調(diào)中出現(xiàn)手動(dòng)檢查的誤差。 (Hello, ).
var ok = require('okay') request.get('/accounts', ok(console.error, (response)=>{ try { data = JSON.parse(response) } catch(error) { console.error(error) } }))
返回回調(diào)或使用 if … else
Node 是并發(fā)的。因此它有個(gè)特點(diǎn)就是,如果不加以注意,就會(huì)出現(xiàn) bug。安全起見,我們使用 return 語句終止執(zhí)行:
let error = true if (error) return callback(error) console.log('I will never run - good.')
避免由于錯(cuò)誤的控制流導(dǎo)致的一些無意的并發(fā)(和失敗操作)。
let error = true if (error) callback(error) console.log('I will run. Not good!')
確保返回一個(gè)回調(diào),以防止繼續(xù)執(zhí)行。
監(jiān)聽錯(cuò)誤事件
幾乎所有的 Node 類/對(duì)象都擴(kuò)展了事件發(fā)射器(觀察者模式)并發(fā)出錯(cuò)誤事件。 在錯(cuò)位被破壞之前,這給開發(fā)人員提供了捕獲錯(cuò)誤并處理的機(jī)會(huì)。
養(yǎng)成使用 .on() 為錯(cuò)誤創(chuàng)建事件偵聽器的好習(xí)慣
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 開發(fā)者和前端開發(fā)者都知道—— save(npm install 參數(shù))可以安裝一個(gè)模板,但要在 package.json 記錄模板的版本。當(dāng)然,還有 save-dev, 用于在 devDependencies 添加記錄 (記錄生產(chǎn)中不需要的模板)。但你知道用 -S 和 -D 代替 --save 和 --save-dev 嗎?你不妨試試。
在安裝模塊的時(shí)候,去刪除 -S 和 -D 為你添加的那些 ^ 記號(hào)。它們非常危險(xiǎn),因?yàn)樗鼈冊(cè)试S npm install(或簡寫為 npm i)從 npm 庫中拉取最新的小版本(語義化的版本號(hào)中的第2個(gè)數(shù))。比如從 v6.1.0 到 v6.2.0 就是一個(gè)小版本發(fā)布。
npm 團(tuán)隊(duì)信任 ,但你不能。他們加上 ^ 符號(hào)是因?yàn)樗麄兿嘈砰_源作者不會(huì)在小版本中引入破壞性的修改。然而明眼人都知道它是不可靠的。你應(yīng)該鎖定版本號(hào),最好使用 shrinkwrap:npm shrinkwrap 創(chuàng)建一個(gè)包含依賴的具體版本的新文件。
結(jié)語
這篇博客只是第一部分,其中已經(jīng)涵蓋了很多內(nèi)容,從回調(diào)函數(shù)和異步代碼的使用,到錯(cuò)誤的檢查和依賴的鎖定。希望這篇文章能給你帶來新的啟示。(作者:Azat Mardan ;翻譯:可譯網(wǎng))
更多行業(yè)資訊,更新鮮的技術(shù)動(dòng)態(tài),盡在。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn