本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫
筆者之前陸陸續續接手過幾個
nodejs
項目, 也參與過幾個有點意思的
nodejs
開源項目, 最近把其中遇到的一些問題和解決方案做一個梳理, 避免大家繼續踩坑. 話不多說我們開始吧~
1. window和mac下設置NODE_ENV變量的問題
我們都知道在前端項目中會根據不同的環境變量來處理不同的邏輯, 在
nodejs
中也一樣, 我們需要設置本地開發環境, 測試環境, 線上環境等, 此時有一直設置環境變量的方案是在
package.json
中的
script
屬性中設置, 如下:
"scripts": {"start": "export NODE_ENV=development && nodemon -w src --exec \"babel-node src\"", "build": "babel src --out-dir dist", "run-build": "node dist", "test": "echo \"Error: no test specified\" && exit 1" }
從
start
指令中我們可以發現我們用
export NODE_ENV=development
來定義開發環境的環境變量,由於筆者採用的是
mac
電腦,所以可以用
export
來定義一個
node
環境變量. 但是在和朋友合作開發項目時發現執行
yarn start
後會報錯, 後面看錯誤信息才發現
window
下不識別
export
, 後面筆者發現
window
定義環境變量可以用
set
, 所以對於
window
用戶, 如果你使用了以上方法設置
NODE_ENV
, 可以採用如下方式:
"scripts": {"start": "set NODE_ENV=development && nodemon -w src --exec \"babel-node src\"" }
2. 執行npm install發生node-gyp報錯的問題
在項目開發過程中有時候拉取新的
node
項目代碼後執行
npm install
, 會報如下錯誤:
node-gyp
就是在
node
環境中使用的生成不同平臺不同編譯器的項目文件, 如果你遇到了相同的問題, 我們可以採用如下方案:
npm install -g node-gyp
或者直接刪除
package-lock.json
或者
yarn.lock
, 然後重新
yarn install
或者
npm install
即可, 筆者親測有效.
3. node + koa2項目中刪除已設置的cookie的解決辦法
由於HTTP是無狀態協議,所以需要cookie來區分用戶之間的身份。我們可以把cookie作為是一個由瀏覽器和伺服器共同協作實現的規範。
cookie
的處理分為以下3步(基礎且重要的知識):
伺服器向客戶端發送cookie瀏覽器將cookie保存(可以在後端設置expires或者maxAge,以session形式存在)每次瀏覽器都會將之前設置好的cookie發向伺服器在開發
node
後臺項目時我們經常涉及用戶管理模塊, 這意味我們需要對用戶進行登錄態管理, 在用戶退出時能及時刪除用戶的
cookie
, 好在
koa2
自帶了處理
cookie
的方法, 我們可以通過如下的方式設置
cookie
:
router.post(api.validVip,async ctx => { ctx.cookies.set('vid', 'xuxiaoxi', { maxAge: 24 * 3600 * 1000 }); });
以上我們隨便設置了一個有效期為1天的
cookie
, 那如果業務有變動, 需要在有效期內清空此
cookie
, 我們該如何處理呢? 解析來給出一個相對可用的解決方案:
ctx.cookies.set('vid', '', { maxAge: 0 });
此時客戶端的
cookie
將在下次請求時自動失效.
4. socket.io如何與koa/egg配合使用
我們都知道完整的
socket.io
通信由兩部分組成:
與NodeJS HTTP 伺服器集成(或安裝在其上)的socket.io在瀏覽器端加載的客戶端庫socket.io-client如果我們直接使用
koa
或者
egg
, 我們需要將它們內部集成的
http
和
socket.io
做兼容, 此時我們可以這樣處理:
import koa from 'koa';import http from 'http';const app = new koa();const server = http.createServer(app.callback());const io = require('socket.io')(server);// 正常的業務處理// ioio.on('connection', (socket) => {console.log('a user connected'); socket.on('doc load', (msg) => { console.log('doc load', msg) io.emit('getData', users) }) });server.listen(3000, () => { // ...});
通過以上的方式就可以正常的將
koa
和
socket.io
做兼容. 後面我們就可以正常的開發
IM
應用啦~
5. 由於nodejs第三方模塊依賴特定node版本導致的報錯解決方案
這個情況筆者之前也遇到過, 主要原因是第三方沒有和
node
版本做到很好的向後兼容, 此時解決方案就是更新此第三方包到最新版本(如果還在維護的情況), 或者使用
node
包管理工具(n)切換到適配的
node
版本, 如下:
// 更新最新的包npm i xxx@latest// 使用包管理工具nnpm i -g n
使用n可以很方便的管理
node
版本, 感興趣可以嘗試一下.
6. nodejs如何創建定時任務
定時任務在後端開發中是很常見的功能之一, 其本質是根據時間規則,系統在後臺自動執行相應的任務. 在
java
,
PHP
等後臺語言中有很豐富的定時任務的支持, 對於
nodejs
這個興起之秀來說, 雖然沒有那麼成熟的生態, 但是仍然有定時任務的模塊, 比如
node-schedule
.
Node Schedule 是用於Node.js的靈活的 cron 類和非 cron 類作業調度程序。它允許我們使用可選的重複規則來安排作業(任意函數)在特定日期執行。它在任何給定時間僅使用一個計時器(而不是每秒鐘/分鐘重新評估即將到來的作業)。
一個很實用的場景是我們想在每年的雙十一或者雙十二讓
node
程序自動抓取某電商的「商品羊毛」, 並推送到自己的郵箱, 此時我們就可以用
Node Schedule
來開啟一個定時任務來執行我們的業務操作, 筆者的很多
node
應用都採用了類似的模式.感興趣可以互相交流一下.
那什麼是
cron
風格的Scheduling呢? 其
github
上給出了一個簡單的介紹:
所以我們可以像如下方式這樣來寫一個定時任務:
let schedule = require('node-schedule');let testJob = schedule.scheduleJob('42 * * * *', function(){console.log('將在未來的每個時刻的42分時執行此代碼, 比如22:42, 23:42');});
7. 在nodejs項目中使用import, export和修飾器@decorator語法
我們都知道現在
nodejs
版本已經到14.0+版本了, 對最新的es語法支持的也足夠好, 但是目前仍然有一些語法不支持, 比如es的模塊導入導出(
import
,
export
), 裝飾器(
@decorator
)等, 此時我們要在
node
項目中使用這些新特性, 我們就不得不藉助工具, 這裡筆者採用
babel7
來解決上述問題, 如下:
# .babelrc{"presets": [ [ "@babel/preset-env", { "targets": { "node": "current" } } ] ], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose" : true }] ]}
我們只需要在項目根目錄裡新建並寫入如上文件, 並安裝
babel
對應的模塊即可, 如下:
yarn add @babel/cli @babel/core @babel/node @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/preset-env
此時就可以想寫前端項目一樣使用這些新語法特性啦~
8. nodejs中優雅的處理json文件以及提高json讀寫性能
對於
nodejs
優化方面其實有很多要聊的, 這裡主要來說說
json
相關的優化方案. 我們需要從2個方面來優化, 一個就是
json
文件的讀寫性能, 此時我們可以採用
fast-json-stringify
來大大提高
json
的讀寫速度, 其本質是提供了一套
json-schema
約束, 讓
json
結構更加有序, 從而提高
json
的讀取查詢速度. 如下使用方式:
const fastJson = require('fast-json-stringify')const stringify = fastJson({title: 'H5 Dooring Schema', type: 'object', properties: { firstName: { type: 'string' }, lastName: { type: 'string' }, age: { description: 'Age in years', type: 'integer' }, reg: { type: 'string' } }})
比如說在H5-Dooring的後臺中, 有很多需要頻繁讀寫
json
數據的接口, 此時使用
fast-json-stringify
對讀寫性能會有很大的提升.
另一方面, 我們在
node
端操作json, 如果用原生的寫法會非常麻煩, 此時我們最好自己對json讀取進行封裝來提高代碼的簡約性, 或者我們直接使用第三方庫
jsonfile
來輕鬆讀寫
json
文件, 如下使用案例:
const json = require('jsonfile')const fileName = 'h5-dooring.json'const jsonData = jsonFile.readFileSync(fileName)
9. nodejs讀取大文件報錯解決方案
在
nodejs
中 我們可以使用兩種方式來讀寫文件, 如下:
fs.readFile() 一次性將文件讀取進內存中, 如果文件過大會導致node內存不夠而報錯fs.createReadStream() 以文件流的方式讀取, 此時可以不用擔心文件的大小由以上介紹可知如果我們要讀取的文件可能會很大(比如視頻等大文件), 我們一開始就要使用fs.createReadStream(), 其實如果我們需要對文件進行解析, 比如要對簡歷等文件進行逐行解析提取關鍵語料, 我們可以使用
node
的
readline
模塊, 此時我們就可以對文件進行逐行讀取並解析, 如下案例:
const fs = require("fs");const path = require("path");const readline = require("readline");const readlineTask = readline.createInterface({input: fs.createReadStream(path.join(__dirname, './h5-dooring')),});readlineTask.on('line', function(chunk) { // 讀取每一行數據});readlineTask.on('close', function() { //文件讀取結束的邏輯}
10. nodejs如何開啟gzip優化網站性能
對於
nodejs
開啟
gzip
的操作也屬於node性能優化的一部分, 經過這樣的處理可以讓我們的網站加載更快, 我們可以使用
koa
的
koa-compress
中間件來實現
gzip
功能. 具體實現如下:
import koa from 'koa';import compress from 'koa-compress';const app = new koa();// 開啟gzipconst options = { threshold: 2048 };app.use(compress(options));
當然
koa-compress
還有很多自定義的配置項, 大家可以感受一下.
11. 解決window和linux系統下路徑分隔符不一致的問題
這個問題也是系統之間的差異導致的, 也是需要考慮的問題, 我們都知道在
linux
系統下路徑的分隔符為
/
, 比如
h5-dooring/src/pages
, 但是在
window
下解析的可能就是
h5-dooring\\src\\pages
這樣的路徑, 此時我們需要做適配, 不然我們部署到不同系統上報錯是必然的, 所以我們需要全局配置路徑通配符, 筆者的解決方案如下:
import os from 'os'const _$ = (os.platform().toLowerCase() === 'win32') ? '\\' : '/';
此時涉及到具體路徑的地方我們用
_$
代替即可, 以上代碼我們用到了
node
的
os
模塊, 感興趣的可以研究一下, 我們可以用
os
模塊處理很多有意思的因為系統差異導致的問題.
12. nodejs如何實現父子進程通信
由於
nodejs
是單線程的, 但是有時候我們需要支持處理多個進程的業務, 目前
nodejs
可以通過哦父子進程的模式來模擬多進程, 我們可以用到
child_process
, 大致流程如下:
筆者之前分享的很多
node
實戰項目都採用了
child_process
, 大致實現過程如下:
// child.jsfunction computedTotal(arr, cb) {// 耗時計算任務}// 與主進程通信// 監聽主進程信號process.on('message', (msg) => { computedTotal(bigDataArr, (flag) => { // 向主進程發送完成信號 process.send(flag); })});// main.jsconst { fork } = require('child_process');app.use(async (ctx, next) => { if(ctx.url === '/fetch') { const data = ctx.request.body; // 通知子進程開始執行任務,並傳入數據 const res = await createPromisefork('./child.js', data) } // 創建異步線程 function createPromisefork(childUrl, data) { // 加載子進程 const res = fork(childUrl) // 通知子進程開始work data && res.send(data) return new Promise(reslove => { res.on('message', f => { reslove(f) }) }) } await next()})
13. node端實現圖片編輯/壓縮
圖片編輯壓縮在很多場景中用前端的技術實現比較常見, 其實在
node
端也有很多需要處理的圖片需要, 畢竟客戶端處理的質量不好控制, 此時我們可以採用
node-images
, 他是一款
node
端輕量級跨平臺圖像編解碼庫, 其主要特性如下:
輕量級:無需安裝任何圖像處理庫。跨平臺:Windows下發布了編譯好的.node文件,下載就能用。使用簡單:jQuery風格的API,簡單可依賴我們可以使用它來裁剪, 壓縮圖片, 基本使用如下:
const images = require("images");images("input.jpg") //加載圖像文件 .size(400) //等比縮放圖像到400像素寬 .draw(images("logo.png"), 10, 10) //在(10,10)處繪製Logo .save("output.jpg", { //保存圖片到文件,圖片質量為50 quality : 50 });
在H5-Dooring 編輯器中哦你也使用了它來做圖片處理和編輯, 大家也可以更根據實際業務來使用.
14. node端解析「命令行指令字符串」實現線上自動打包部署項目
關於
node
解析
cmd
字符串並執行命令行指令的方式筆者之前在寫自己實現一個自動化工作流的文章中也介紹過, 使用了
child_process
模塊的
exec
, 具體實現可以參考文章:
基於NodeJS從零構建線上自動化打包工作流(H5-Dooring特別版)
這裡寫一個簡單的例子:
const cmdStr = `cd ${outWorkDir} && yarn build ${fid}`// 解析命令行指令, 實現線上自動打包構建項目exec(cmdStr, function(err, stdout, stderr){if(err) { console.log('api error:'+stderr); io.emit('htmlWorked', { result: 'error', message: stderr }) } else { // ... }})
15. 如何解決node應用崩潰, 負載均衡和進程管理
解決此問題最好的方式就是採用
pm2
或者
forever
, 其提供了強大的
node
進程管理, 負載均衡的能力, 並提供了一定程度的應用監控, 建議在線上環境使用
pm2
來管理我們的
node
應用.