Node.js + Webpack5 后端开发 构建自动重载服务器
Webpack5的变化
- 前端开发变化, 开箱即用
webpack serve
命令,只需要安装webpack-dev-server就可以直接启用了。
$ webpack serve --help
Usage: webpack s | serve
Description: Run the webpack Dev Server
此时,npx webpack-dev-server
命令会报错,因此它只兼容Webpack4,不过Webpack5可以使用它(webpack serve
命令就是调用它):
$ npx webpack-dev-server
Cannot find module 'webpack-cli/bin/config-yargs'
Require stack:
- C:\Users\ly\Desktop\TypeScript\node_modules\webpack-dev-server\bin\webpack-dev-server.js
webpack-node-externals库
使用Webpack开发后端时,通常不希望捆绑node_modules下的依赖项。该库创建了一个外部函数,使Webpack中捆绑时将忽略node_modules下的模块。
Github:https://github.com/liady/webpack-node-externals
安装:
npm install webpack-node-externals -D
yarn add webpack-node-externals -D
原理:externals作为一个函数使用 -- https://webpack.js.org/configuration/externals/#function
externals: [
// 'express',
function ({ context, request }, callback) { // 官网和CLI提示不一致,此处遵循CLI标准
console.log(`${context} => ${request}`); // 打印import和require请求
callback(); // 通过请求
},
],
例子:该函数过滤express
模块,该模块的导出语句直接替换为require('express')
function ({ context, request }, callback) {
if (!context.match(/.*node_modules.*/)) {
console.log(`请求:${context} => ${request}`);
if (request === 'express') {
callback(null, `require('express')`); // 直接编码`const express = ?`赋值语句的右值
} else {
callback();
}
} else {
console.log(`其它:${context} => ${request}`);
callback();
}
},
该怎么实现webpack-node-externals库的功能呢?
观察发现express导致了更多的require请求,这些请求的上下文都是在node_modules目录下,但是不能拒绝这些请求,否则无法生存dist:
console.log(`其它:${context} => ${request}`);
// callback(); 忽略请求,编译被中断
其实,对于node_modules目录下的请求,使用require('${contextByNode_Modules}/${request}')
语句是可以减轻编译重量的:
externals: [
// 'express',
function ({ context, request }, callback) { // 官网和CLI提示不一致,此处遵循CLI标准
if (!context.match(/.*node_modules.*/)) {
console.log(`导入:${context} => ${request}`);
callback();
} else {
let contextByNode_Modules = context.match(/.*node_modules[\/\\](.*)$/)[1];
console.log(`其它:${context} => ${request}`);
let cmd = `require('${contextByNode_Modules}/${request}')`;
console.log({ cmd });
callback(null, cmd);
}
},
],
但是这些模块还是被Webpack解析了(例中的express),并且被部分绑定(哭),达不到“由宿主提供提供该库”的要求。
相对合理的解决方案
function ({ context, request }, callback) { // 官网和CLI提示不一致,此处遵循CLI标准
console.log(`导入:${context} => ${request}`);
// return callback(); // 临时关闭函数以测试
// 排除node_modules模块以及loader
if (!path.isAbsolute(request) && !request.match(/^\./)) { // require目标不是绝对路径,且不以dot开头,则大概率就是node_modules请求。
// 要排除入口,直接遍历config.entry字段似乎不太可行,因为有隐含规则,比如"./src/index.js"。
// 上下文不可以是cwd,或者__dirname,从而排除入口(此处担心入口是绝对路径,且不以dot开头,这样会造成编译完成但运行时找不到模块)
if (context !== process.pwd && context !== __dirname) {
// 还有一种情况:在import或require语句中指定了loader,而指定方式又有强制与非强制的区别。
// 一般来说,即便这些依赖项可以在运行时加载,不过我们也希望进行编译打包,从而方便开发
// 如果指定了loader,则一定含有感叹号!这种情况需要排除掉
if (!request.includes('!')) {
// 替换node_modules依赖请求
let instruction = `require('${request}')`;
console.log('运行时依赖:', `${request}由node_modules提供 => ${request} = ${instruction}`.green);
return callback(/*没有错误*/null, instruction);
}
}
}
return callback();
},
自动重载以应用代码更新
Github: https://github.com/develon2015/node-dev-server
想必大家都知道前端开发工具: webpack-dev-server
, 本人实现了一个类似的功能, 不过它在后端开发中发挥作用!
值得一提的是,@nest/cli也是使用的webpack实现,并且也使用了第三方开源库设置externals来排除node_modules模块。