前言
以前在用 koa 写 server 的时候, 发布简直是噩梦. 需要将 src 里面的全部文件都覆盖掉, config 配置文件也要覆盖, 稍有不慎就会线上报各种各样的问题, 然后就得回退, 本地调好在发布. 偶然看见一篇文章讲 如何使用 webpack 打包 koa app , 惊为天人, 原来 webpack 也能打包后台. 这在以前想都没想过.
关键问题
一: 所有 node_modules 里的模块都不进行打包
webpack 的核心功能是将引用的各个模块打到一个文件里, 并会将各种规范的模块进行统一的模块化处理 (webpack 规范).
然而 node 中包含大量的 fs,path 操作, 这些 fs 和 path 操作在打包完成后将没有操作对象, 还会报出很多各样的错误.
所以使用 webpack 打包的核心就是拒绝打包一切 node_modules 里的模块, 只是将相对路径引用的文件打包到一个文件里. 恰巧我们发现 webapck 提供 externals 属性来排除掉不需要打包的模块.
再深入点我们可以发现: 像 webpack,nodemon,babel-preset-env 这样的模块是 app 开发环境依赖的包, 我们的程序里根本不会 require 这些模块.
综上可以发现: 我们只将所有 require 到的包排除掉就可以了, 这个模块对应的也就是 package.json 里 dependencies 下的模块. 有关 dependencies 和 devDependencies 的区别要理解好.
因此我们可以使用 externals-dependencies 这个插件配合 externals 属性实现 dependencies 的排除工作.
代码:
const webpack = require('webpack');
const _externals = require('externals-dependencies')
module.exports = {
...
externals: _externals(),
...
}
二: target 指向 node
官方文档: 编译为类 Node.js 环境可用(使用 Node.js require 加载 chunk)
代码:
target: 'node',
三: 增加 node 配置
官方文档: 这些选项可以配置是否 polyfill 或 mock 某些 Node.js 全局变量和模块. 这可以使最初为 Node.js 环境编写的代码, 在其他环境 (如浏览器) 中运行.
代码:
node: {
console: true,
global: true,
process: true,
Buffer: true,
__filename: true,
__dirname: true,
setImmediate: true,
path: true
},
四: babel 配置
为了兼容低版本的 node 不原生支持 async/await 的问题. 这里 babel 我使用了 babel-preset-env{"modules": false} 的配置. 此配置会将 es6 语法转为 es5 语法, 例如 let,const 转为 var.
同时将所有的 async/await 函数也转成了 polyfill 里定义的_asyncToGenerator 函数.
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
其实是使用 promise 实现了 async 函数的功能.
当然这个函数在运行时还需要 regeneratorRuntime 函数. 所以我在全局引入了 babel-polyfill 来提供 regeneratorRuntime 函.
注: 如果你的 node 版本很高且原生支持 async/await, 大可将 babel-preset-env 和 babel-polyfill 省略掉
代码:
const path = require('path');
const webpack = require('webpack');
const _externals = require('externals-dependencies') module.exports = {
entry: {
app: [
// 如果 polyfill 放在这里, 打包的时候将不会被 external, 必须在 js 里 require 才能有效 external
// 'babel-polyfill',
'./src/index.js']
},
output: {
path: path.resolve(__dirname),
filename: '[name].js'
},
resolve: {
extensions: [".js"]
},
target: 'node',
externals: _externals(),
context: __dirname,
node: {
console: true,
global: true,
process: true,
Buffer: true,
__filename: true,
__dirname: true,
setImmediate: true,
path: true
},
module: {
rules: [{
test: /\.js/,
use: ['babel-loader']
}]
},
plugins: [new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
}), ]
}
部署
经过打包, 部署的时候就方便多了, 只需要将 package.json,app.js, 以及 view 里的 html 部署上线就好了. 然后在服务器上执行
.npm install.npm run
for
然后 server 就后台运行了. 如果需要更新发布, 只需要本地重新 npm run dev 或 npm run build 打好包, 拖到服务器覆盖 app.js 即可.
脚手架项目地址
来源: https://juejin.im/post/5a71dd2351882573520db90e