Node JS后端项目开发与生产环境总结
- 作者:Bougie
- 创建于:2018-05-02
- 更新于:2023-03-09
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 (opens new window)在 js 文件变化后悔重新运行程序,在package.json
的scripts
中添加:
dev: 'nodemon server.js'
npm run dev
nodemon 还有许多可选配置,具体参阅nodemon 文档 (opens new window)
# 错误处理
以 koa 为例
app.on('error', (err) => {
log.error('server error', err)
})
如若想要将错误抛出到浏览器页面和美化错误页面,express
可用express-error-handler (opens new window),koa
可用onerror (opens new window)
# 前端 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 (opens new window),然后在package.json
的scripts
中添加:
"watch": "cross-env NODE_ENV=development webpack --watch webpack.config.js"
开发时应运行两个命令:
// nodemon
npm run dev
// webpack
npm run watch
# 生产环境
生产环境一般使用pm2 (opens new window),pm2
已经帮我们完成了进程守护和负载均衡,内部实现原理在此不再赘述,具体参考pm2 文档 (opens new window)。
// 生成pm2配置文件ecosystem.config.js
pm2 ecosystem
生成的配置文件已包含了生产环境的基本本质。跟多配置请参考pm2 文档 (opens new window),在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