0%

Webpack取消对reuqire处理的几种方式

群里交流的时候我碰到了个需求,是凌寒初见(资深全栈工、客户端开发工程师)大佬提出来的。

需要将对应文件夹文件打包输出到对应文件夹,但其中我碰到了require问题。

webpack 遇到 require 会自动处理引入的文件。

比如我引入了 fs-extra 模块

1
const fs = require("fs-extra");

然后 webpack 就会将里面很多方法抽出来,打包到文件里面。

但是某些场合下,我并不想处理,只想打包后仍能正常引入,因为 Nodejs 环境本身就是 CommonJS 模块系统,原生支持 reuquire

我期望打包前打包后,都可以直接用 require('fs-extra'),而代码不变。

这是我总结的几种 Webpack 取消对 reuqire 处理的几种方式,建议大家在读这篇文章之前能看一下下面的相关资料和参考文章。

常见解决方法

Target 修改为 node

这个只对 nodejs 环境有效,并且只排除 node 官方常见的依赖:Target

Compile for usage in a Node.js-like environment (uses Node.js require to load chunks)

在 webpack 配置环境中加入以下参数

1
target: "node",

当你引入 fs path 等模块的时候,webpack 不会对 require 等方法进行处理

1
2
const fs = require("fs");
const fs = require("path");

但对于 fs-extra 这样的外部模块,webpack 仍然会抽出其中的方法进行处理。但官方也给出了排除的方法。

通过 Externals 进行排除

建议看一下官方文档:Externals

有以下几种方式,我都介绍一下,机翻你们看着理解。

string

例如,如果您想从输出依赖中排除 fs-extra,并在运行时导入它

1
2
3
4
5
6
module.exports = {
// ...
externals: {
"fs-extra": "commonjs2 fs-extra",
},
};

导出后效果(贴部分代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function (e, t, r) {
let { Hi: n } = r(1);
const o = r(2);
console.log("Hello " + n()), o.writeFile("123.txt", "123");
},
function (e, t) {
e.exports = {
Hi: function (e) {
return "World" + e;
},
};
},
function (e, t) {
e.exports = require("fs-extra");
},

可以看到最后的 3 行,没有将 fs-extra 模块进行处理,而是直接 require

object

不多说,贴代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module.exports = {
//...
externals: {
react: "react",
},

// or

externals: {
lodash: {
commonjs: "lodash",
amd: "lodash",
root: "_", // indicates global variable
},
},

// or

externals: {
subtract: {
root: ["math", "subtract"],
},
},
};

英语不好,机翻也不准,你们看着理解吧

This syntax is used to describe all the possible ways that an external library can be made available. lodash here is available as lodash under AMD and CommonJS module systems but available as _ in a global variable form. subtract here is available via the property subtract under the global math object (e.g. window[‘math’][‘subtract’]).

function

这个方法非常有用,后面忽略文件用到了它

具体用法很多,建议看官方文档:Function

示例代码,正则匹配到某个模块,就忽略,否则就处理编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
//...
externals: [
function (context, request, callback) {
if (/^yourregex$/.test(request)) {
// Externalize to a commonjs module using the request path
return callback(null, "commonjs " + request);
}
// Continue without externalizing the import
callback();
},
],
};

对引入的文件进行忽略

如果想对引入的文件进行忽略怎么办?这个需求就非常少见了,或者说几乎碰不到。

但确实有这样的场合需要,比如动态引入。

1
2
3
4
5
6
7
8
9
10
const { Hi } = require("./test");
const fs = require("fs-extra");

// 哈喽
function Hello() {
console.log("Hello " + Hi());
fs.writeFile("123.txt", "123");
}

Hello();

然后 webpack 处理以后变了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function (e, t, r) {
let { Hi: n } = r(1);
const o = r(2);
console.log("Hello " + n()), o.writeFile("123.txt", "123");
},
function (e, t) {
e.exports = {
Hi: function (e) {
return "World" + e;
},
};
},
function (e, t) {
e.exports = require("fs-extra");
},

我仍然想输出 require("./test"); ,研究之后总结了下面的几个方法。

剑走偏锋 - 使用 non_webpack_require

看了 github 上的 issues ,大佬们提供了这样的方法:__non_webpack_require__

也就是你的代码里面, require("fs-extra") 替换为 __non_webpack_require__("fs-extra")

上面的方法,还开发出了脚本和插件,参考代码如下

1
2
3
4
5
6
const nodeVer = typeof process !== "undefined" && process.versions?.node;
const nodeRequire = nodeVer
? typeof __webpack_require__ === "function"
? __non_webpack_require__
: require
: undefined;

还有这种,代码加入module.rules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
// regex for the files that are problematic
test: /knex\/lib\/migrate\/sources/,
loader: 'string-replace-loader',
options: {
// match a require function call where the argument isn't a string
// also capture the first character of the args so we can ignore it later
search: 'require[(]([^\'"])',
// replace the 'require(' with a '__non_webpack_require__(', meaning it will require the files at runtime
// $1 grabs the first capture group from the regex, the one character we matched and don't want to lose
replace: '__non_webpack_require__($1',
flags: 'g'
}
}

重剑无锋,大巧不工 - 使用 Externals 的 function 进行忽略

我搞了半天没效果,在群里面进行讨论,一位在德国的大佬 kio 对 webpack 有极深的研究,给出了下面的办法,我看了之后,只能 while(1){ console.log('dalao dalao tql');}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const path = require("path");

const entry = "./src/index.js";

module.exports = {
entry,
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
target: "node",
externals: [
function (context, request, callback) {
if (entry !== request) {
return callback(null, "commonjs " + request);
}
return callback();
},
],
};

原代码

1
2
3
4
5
6
7
8
9
10
const { Hi } = require("./test");
const fs = require("fs-extra");

// 哈喽
function Hello() {
console.log("Hello " + Hi());
fs.writeFile("123.txt", "123");
}

Hello();

输出代码

1
2
3
4
5
6
7
8
9
10
11
function (e, t, r) {
let { Hi: n } = r(1);
const o = r(2);
console.log("Hello " + n()), o.writeFile("123.txt", "123");
},
function (e, t) {
e.exports = require("./test");
},
function (e, t) {
e.exports = require("fs-extra");
},

相关资料

参考文章

感谢这些前辈帮忙踩坑,让我有资料可查

资源

webpack 官方引用的 externals:webpack-node-externals

Github 地址

webpack-no-handle-require

总结

学习了 webpack ,收获蛮多的。