![](/img/trans.png)
[英]How can Include both commonJs require syntax and es modules import syntax in the same bundle - webpack
[英]How to include manual import() in Webpack Bundle
我是Webpack的新手,如果那是一个愚蠢的问题,请多多包涵。
我的目标是将基于AMD的旧代码库转换为基于ES6模块的解决方案。 我正在努力处理动态import()
。 因此,我的应用路由器基于模块工作,即每个路由都映射到模块路径,然后require
d。 因为我知道将包含哪些模块,所以我只需将那些动态导入的模块添加到我的r.js配置中,就可以在单个文件中构建所有内容,并且所有require调用仍然有效。
现在,我正在尝试对ES6模块和Webpack进行同样的操作。 在我的devmode中,这没问题,因为我可以将import()
替换为require()
import()
。 但是我无法使它与捆绑一起使用。 无论的WebPack分裂我的代码(现在仍然未能反正加载动态模块),或者-如果我使用Array格式的entry
配置,动态模块包含在捆绑,但负载仍然失败: 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%的解决方案,并且肯定不是为了胆小的人,因为它非常丑陋且脆弱。 但我仍然想与您分享我的方法:
我的路由器使用如下配置文件:
import StaticClass from "/src/app/StaticClass.js";
export default {
StaticClass: {
match: /^\//,
module: StaticClass
},
DynClass: {
match: /^\//,
module: "/src/app/DynClass.js"
}
};
因此,您可以看到导出对象是一个对象,其中的键用作路由的ID,而对象则包含匹配项(基于正则表达式)和如果路由匹配应由路由器执行的模块。 我可以为路由器提供构造函数(或对象),以立即可用(即包含在主块中)模块,或者如果模块值为字符串,则意味着路由器必须通过以下方式动态加载该模块:使用字符串中指定的路径。
因此,据我所知, 可能会加载哪些模块(但是否以及何时加载),现在我可以在构建过程中解析此文件,并将路由配置转换为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只是变成了块的名称!
为此,我使用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”只是一个空的存根模块。 特殊注释之间的部分在构建时会被删除(使用webpack-loader-clean-pragma
加载器),因此当webpack“看到” dummy.js
的导入时,此代码将不会在构建本身中执行,因为isDev
对其求值false
。 并且由于我们已经定义了具有相同路径的虚拟模块,因此在构建虚拟模块时就包括了该模块,当然,所有依赖关系也都得到了解决。
对于开发来说,这很容易:
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
函数以加载正确的块: await __webpack_require__.e(routeId);
。 还记得我在生成虚拟模块时为我的块命名吗? 这就是为什么我现在仍然可以找到它们!
我遇到的另一件事是,当几个动态加载的模块具有相同的依存关系时,webpack尝试生成更多名为诸如module1~module2.bundle.js
类的块,从而破坏了我的构建。 为了解决这个问题,我需要确保所有这些共享模块都放入一个名为“ shared”的特定命名包中:
optimization: {
splitChunks: {
chunks: "all",
name: "shared"
}
}
在生产模式下,我可以简单地手动加载此块,然后再根据它请求任何动态模块:
if (!isDev) {
await __webpack_require__.e("shared");
}
同样,此代码仅在生产模式下运行!
最后,我必须防止webpack将模块(和大块)重命名为“ 1”,“ 2”等,而是保留我刚刚定义的名称:
optimization: {
namedChunks: true,
namedModules: true
}
是的,那里有! 正如我所说的那样,这看起来并不漂亮,但似乎可以正常工作,至少在简化的测试设置中如此。 我真的希望当我完成所有其他工作时(例如ESLint,SCSS等),我面前没有任何障碍!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.