我正在开发一个Web应用程序,其中包含应使用dynamic imports延迟加载的小部件。 每个窗口小部件都应为其收到单独的捆绑包,并且它们的依赖关系仅在用户请求时才由浏览器获取。有一个例外:流行的窗口小部件以及它们使用或扩展的库类应包括在主捆绑包中。在下面,您将看到我打算实现的文件夹结构以及我想要的输出:
File Structure Desired chunk
src/
├── widgets/
│ ├── popular-widget/index.ts main
│ ├── edge-case-widget/index.ts edge-case-widget
│ └── interesting-widget/index.ts interesting-widget
├── library/
│ ├── Widget.ts main
│ └── WidgetFactory.ts main
└── index.ts main (incl. entry point)
dist/
├── edge-case-widget.app.js edge-case-widget
├── interesting-widget.app.js interesting-widget
└── main.app.js main (incl. entry point)
要延迟加载小部件,我在create
的{{1}}方法中使用以下表达式。效果很好,Webpack神奇地拾取了小部件模块。
WidgetFactory
我试图通过在Webpack中配置const Widget = await import(`../widgets/${name}/index`)
来解决代码拆分难题。通过提供optimization.splitChunks.cacheGroups
函数,我将模块分配到test
或library
缓存组中。
widgets
我被卡住了!
module.exports = {
entry: './src/index.ts',
[...]
optimization: {
splitChunks: {
cacheGroups: {
library: {
test: module => isInLibrary(module.identifier()),
name: 'library',
chunks: 'all',
minSize: 0
},
widgets: {
test: module => isWidgetBundle(module.identifier()),
name: module => widgetNameFromModuleId(module.identifier()),
chunks: 'async',
minSize: 0
}
}
}
}
}
缓存组上使用chunks: 'async'
可使某些库类进入library
块,而另一些则保留在main
块中。为什么?library
缓存组上使用chunks: 'all'
时,所有模块都正确地合并到其中,但是我丢失了library
块上的入口点并得到空白页。怎么样?main
缓存组重命名为library
时,丢失了入口点as documented here。我不太了解为什么会这样,以及如何将main
入口点和main
合并到一个捆绑包中。希望您可以通过Webpack阐明我的陡峭学习曲线。
答案 0 :(得分:1)
根据我通常在Webpack(v4)配置中使用的配置,我将通过以下方式进行配置:
splitChunks: {
chunks: 'async',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: true,
cacheGroups: {
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
library: {
test: module => module => isInLibrary(module.identifier()),
chunks: 'all',
enforce: true
},
}
}
如果只是懒惰地需要窗口小部件,请不要将widgets
添加到cacheGroups。
Webpack应该为每个异步需求解析库中的窗口小部件依赖项。
我没有尝试强制使用捆绑包名称,但我认为它的行为将相同。
答案 1 :(得分:1)
使用所需的配置创建了一个简单的github repo。
test
和name
函数更改为您的函数) splitChunks: {
cacheGroups: {
widgets: {
test: module => {
return module.identifier().includes('src/widgets');
},
name: module => {
const list = module.identifier().split('/');
list.pop();
return list.pop();
},
chunks: 'async',
enforce: true
}
}
}
main
中,则不应动态导入它。使用常规的import
语句。因为webpack总是会为任何动态导入创建一个新块。因此,请看一下this文件。 import { Widget } from '../widgets/popular-widget';
export class WidgetFactory {
async create(name) {
if (name === 'popular-widget') return await Promise.resolve(Widget);
return await import(`../widgets/${name}/index`)
}
}
UPD
更改了配置,以使相关的node_modules与小部件保持在一起。
我为创建新的小部件块编写了两个简单的函数。
这里的技巧是module.issuer
属性,这意味着谁导入了此文件。因此,函数isRelativeToWidget
对于在小部件的文件结构的任何深度导入的任何依赖项(无论是否有node_modules)都将返回true。
可以找到代码here。
产生的配置:
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: false,
default: false,
edgeCaseWidget: getSplitChunksRuleForWidget('edge-case-widget'),
interestingWidget: getSplitChunksRuleForWidget('interesting-widget')
}
}
function isRelativeToWidget(module, widgetName) {
if (module.identifier().includes(widgetName)) return true;
if (module.issuer) return isRelativeToWidget(module.issuer, widgetName)
}
function getSplitChunksRuleForWidget(widgetName) {
return {
test: module => isRelativeToWidget(module, widgetName),
name: widgetName,
chunks: 'async',
enforce: true,
}
}