原文地址:Node JS后端項目開發與生產環境總結
Node JS常用后端框架有express、koa、sails。國產框架有個egg js,已經在cnode投入生產了,還有個think js,類似think php,在此支持一波。每個框架在開發環境與生產環境都有所不同,這里以
koa
為例
開發環境與生產環境的區別
建立在后台模板渲染(ejs, pug)的基礎上。前后分離架構請參考webpack熱更新實現
開發環境
- 熱更新
- 錯誤處理
- 前端js代碼自動打包
生產環境
- 靜態緩存(static cache)
- 內容壓縮(gzip)
- 日志文件
- 進程守護
- 強制https
- 404處理
- 負載均衡
- 前端js代碼混淆壓縮
開發環境配置
熱更新(nodemon)
nodemon在js文件變化后悔重新運行程序,在package.json
的scripts
中添加:
dev: 'nodemon server.js'
npm run dev
nodemon還有許多可選配置,具體參閱nodemon文檔
錯誤處理
以koa為例
app.on('error', err => {
log.error('server error', err)
});
如若想要將錯誤拋出到瀏覽器頁面和美化錯誤頁面,express
可用express-error-handler,koa
可用onerror
前端js代碼自動打包(webpack)
由於是后台模板渲染,所以沒法用webpack-dev-server
進行自動刷新。能做的就是利用webpack
的watch
在前端js改變后自動打包,當然還是免不了手動刷新
// webpack.config.js
const config = {
entry: {
app: path.resolve(root, './modules/app.js'),
about: path.resolve(root, './modules/about.js'),
},
output: {
path: path.resolve(root, './dist'),
publicPath: path.resolve(root, './dist'),
filename: '[name].js'
},
module: {
rules: [
{
test: /(\.js)$/,
use: {
loader: "babel-loader",
},
exclude: /node_modules/
}
]
},
devtool: '#eval-source-map',
watch: true,
watchOptions:{
poll:1000,//監測修改的時間(ms)
ignored:/node_modules/,//不監測
}
};
注意一定要開啟source-map
,不然無法定位報錯位置。為通知webpack
是生產還是開發環境,可以使用cross-env,然后在package.json
的scripts
中添加:
"watch": "cross-env NODE_ENV=development webpack --watch webpack.config.js"
開發時應運行兩個命令:
// nodemon
npm run dev
// webpack
npm run watch
生產環境
生產環境一般使用pm2,pm2
已經幫我們完成了進程守護和負載均衡,內部實現原理在此不再贅述,具體參考pm2文檔。
// 生成pm2配置文件ecosystem.config.js
pm2 ecosystem
生成的配置文件已包含了生產環境的基本本質。跟多配置請參考pm2文檔,在package.json
文件的scripts
中添加
"prd": "pm2 start ecosystem.config.js --env production"
生產環境下運行
npm run prd
這時我們可以通過process
全局變量獲取到環境狀態,在app.js
中添加
const prdEnv = process.env.NODE_ENV == 'production'
靜態緩存
const staticCache = require('koa-static-cache')
if(prdEnv) {
// 靜態緩存
app.use(staticCache(path.join(__dirname, 'public'), {
maxAge: 365 * 24 * 60 * 60
}))
}
gzip
const compress = require('koa-compress')
if(prdEnv) {
// gzip
app.use(compress({
filter: function (content_type) {
return /text/i.test(content_type)
},
threshold: 2048,
flush: require('zlib').Z_SYNC_FLUSH
}))
}
日志文件
類似nginx
的access.log
和error.log
,利用fs
模塊的appendFile
方法來輸出日志。首先在項目根目錄下新建文件夾logs
const fs = require('fs')
const path = require('path')
if(prdEnv) {
// logger
app.use(async(ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
fs.appendFile(path.join(__dirname, 'logs', ctx.status < 400 ? 'access.log' : 'error.log'), `[${start.toLocaleString()}] ${ctx.status} ${ctx.method} ${ctx.url} - ${ms}ms\r\n`)
})
}
強制https
// force https
app.use((ctx, next) => {
if(ctx.protocol == 'http') {
ctx.redirect(ctx.href.replace('http', 'https'))
} else {
return next()
}
})
404處理
建立一個模板命名為notFound.pug
,在路由之后渲染
// 404
app.use(async (ctx) => {
await ctx.render('notFound')
})
前端js代碼壓縮混淆
在webpack中添加插件
// webpack.config.js
// 提取公共js
new webpack.optimize.CommonsChunkPlugin({name: 'common'})
// 壓縮代碼
new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
parallel: true,
mangle: true,
compress: {
warnings: false,
drop_debugger: true,
drop_console: true
}
})
// package.json
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
發布應用時需運行
npm run build
npm run prd