gpt4 book ai didi

javascript - 如何在 Webpack Bundle 中包含手动 import()

转载 作者:行者123 更新时间:2023-11-30 06:17:50 25 4
gpt4 key购买 nike

我对 Webpack 很陌生,如果这是一个愚蠢的问题,请耐心等待。

我的目标是将旧的基于 AMD 的代码库转换为基于 ES6 模块的解决方案。我正在努力处理动态import() s。所以我的应用程序路由器基于模块工作,即每个路由都映射到模块路径,然后 require d。自从我了解将包含哪些模块,我只是将这些动态导入的模块添加到我的 r.js 配置中,并且能够在单个文件中构建所有内容,所有 require 调用仍然有效。

现在,我正在尝试对 ES6 模块和 Webpack 做同样的事情。使用我的 devmode 这没问题,因为我可以替换 require()import() .但是我不能让它与捆绑一起工作。 Webpack 会拆分我的代码(无论如何仍然无法加载动态模块),或者 - 如果我对 entry 使用数组格式config,动态模块包含在捆绑包中,但加载仍然失败:Error: Cannot find module '/src/app/DynClass.js'
这就是我的 Webpack 配置的样子:

const webpack = require('webpack');
const path = require('path');

module.exports = {
mode: "development",
entry: ['./main.js', './app/DynClass.js'],
output: {
filename: 'main.js',
path: path.resolve(__dirname, "../client/")
},
resolve: {
alias: {
"/src": path.resolve(__dirname, '')
}
},
module: {
rules: [
{
test: /\.tpl$/i,
use: 'raw-loader',
},
]
}
};

所以基本上我想告诉 Webpack:“嘿,还有另一个模块(或更多)要动态加载,我希望它包含在包中”

我怎样才能做到这一点?

最佳答案

所以,是的,经过多次摆弄,隧道尽头似乎有一盏灯。尽管如此,这不是一个 100% 的解决方案,而且它肯定不适合胆小的人,因为它非常丑陋和脆弱。但我仍然想与您分享我的方法:
1)手动解析我的路线配置
我的路由器使用如下配置文件:

import StaticClass from "/src/app/StaticClass.js";

export default {
StaticClass: {
match: /^\//,
module: StaticClass
},
DynClass: {
match: /^\//,
module: "/src/app/DynClass.js"
}
};
如您所见,导出是一个对象,其键充当路由 id,一个对象包含匹配项(基于正则表达式)和路由匹配时应由路由器执行的模块。我可以为我的路由器提供一个构造函数(或一个对象),用于立即可用的模块(即包含在主 block 中),或者如果模块值是一个字符串,这意味着路由器必须通过动态加载这个模块使用字符串中指定的路径。
所以我知道哪些模块可能会被加载(但不是如果和何时)我现在可以在我的构建过程中解析这个文件并将路由配置转换为 webpack 可以理解的东西:
const path = require("path");
const fs = require("fs");

let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

routesSource = routesSource.substr(routesSource.indexOf("export default"));
routesSource = routesSource.replace(/module:\s*((?!".*").)*$/gm, "module: undefined,");
routesSource = routesSource.replace(/\r?\n|\r/g, "").replace("export default", "var routes = ");

eval(routesSource);

let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) => {

if (typeof routeConfig.module === "string") {
return acc + `import(/* webpackChunkName: "${routeName}" */"${routeConfig.module}");`;
}

return acc;

}, "") + "export default ''";
(是的,我知道这很丑,也有点脆,所以这肯定可以做得更好)
本质上,我创建了一个新的 虚拟 翻译需要动态导入的每个路由条目的模块,因此:
DynClass: {
match: /^\//,
module: "/src/app/DynClass.js"
}
变成:
import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");
所以路由 id 就变成了 block 的名称!
2)在构建中包含虚拟模块
为此,我使用 virtual-module-webpack-plugin :
plugins: [
new VirtualModulePlugin({
moduleName: "./app/dummy.js",
contents: dummySource
})
],
在哪里 dummySource只是一个字符串,其中包含我刚刚生成的虚拟模块的源代码。现在,这个模块被拉进来,“虚拟导入”可以被 webpack 处理。但是等等,我仍然需要导入虚拟模块,但是在我的开发模式下我没有任何模块(我在本地使用所有东西,所以没有加载器)。
因此,在我的主要代码中,我执行以下操作:
let isDev = false;
/** @remove */
isDev = true;
/** @endremove */

if (isDev) { import('./app/dummy.js'); }
当我处于开发模式时,“dummy.js”只是一个空的 stub 模块。在构建时(使用 webpack-loader-clean-pragma 加载器)删除了特殊注释之间的部分,因此当 webpack “看到” dummy.js 的导入时,从那时起,此代码将不会在构建本身中执行 isDev计算结果为 false .而且由于我们已经定义了具有相同路径的虚拟模块,因此在构建时就像我想要的那样包含了虚拟模块,当然所有的依赖关系也都得到了解决。
3) 处理实际装载
对于开发来说,这很容易:
import routes from './app/routes.js';

Object.entries(routes).forEach(async ([routeId, route]) => {
if (typeof route.module === "function") {
new route.module;
} else {
const result = await import(route.module);
new result.default;
}
});
(请注意,这不是实际的路由器代码,足以帮助我完成我的 PoC)
好吧,但是对于构建我需要别的东西,所以我添加了一些特定于构建环境的代码:
/** @remove */
const result = await import(route.module);
new result.default;
/** @endremove */

if (!isDev) {
if (typeof route.module === "string") { await __webpack_require__.e(routeId); }
const result = __webpack_require__(route.module.replace("/src", "."));
new result.default;
}
现在,开发环境的加载代码刚刚被剥离出来,还有另一个内部使用 webpack 的加载代码。我还检查模块值是函数还是字符串,如果是后者,我调用内部 require.ensure加载正确 block 的函数: await __webpack_require__.e(routeId); .还记得我在生成虚拟模块时命名了我的 block 吗?这就是为什么我现在仍然可以找到它们的原因!
4)还有更多工作要做
我遇到的另一件事是,当几个动态加载的模块具有相同的依赖项时,webpack 会尝试生成更多名称为 module1~module2.bundle.js 的 block 。 ,打破我的构建。为了解决这个问题,我需要确保所有这些共享模块都进入我称为“共享”的特定命名包中:
optimization: {
splitChunks: {
chunks: "all",
name: "shared"
}
}
在生产模式下,我只需在请求任何依赖于它的动态模块之前手动加载这个 block :
if (!isDev) {
await __webpack_require__.e("shared");
}
同样,此代码仅在生产模式下运行!
最后,我必须防止 webpack 将我的模块(和 block )重命名为“1”、“2”等,而是保留我刚刚定义的名称:
optimization: {
namedChunks: true,
namedModules: true
}
是的,你有它!正如我所说,这并不漂亮,但似乎可以工作,至少在我简化的测试设置中是这样。我真的希望当我做所有其他事情(如 ESLint、SCSS 等)时,没有任何障碍在我面前!

关于javascript - 如何在 Webpack Bundle 中包含手动 import(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55052636/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com