这里有一个很长的。尽管我对答案或解决方案的期望不高(或任何东西),但仍然感觉像是一个
我有什么
我有一个有 2 个入口点的应用程序。它们都导入相同的文件,main.ts
又导入一个 Handlebars 模板main.hbs
:
entry1.ts
└ main.ts
├ main.hbs
└ …
entry2.ts
└ main.ts
├ main.hbs
└ …
main.ts
还导入了其他 TS 类(数千个),这些类导入了其他 HBS 模板(数百个),但几乎所有这些模板都在两个条目之间共享。这些条目只是使用一些特定于条目的选项从main.ts
调用 Main 类。
我需要什么
我被要求为 HBS 模板创建一个特定于条目的"变体",以便entry1
加载main.v1.hbs
并且entry2
加载main.v2.hbs
,如果存在此类文件。如果任何其他导入的.ts
文件导入任何其他.hbs
文件,它们还将返回相应的*.v1.hbs
/*.v2.hbs
变体。
由于我希望它尽可能自动化,并且对实际源代码的更改尽可能少,因此我决定要走的方法是让 Webpack "重定向"导入:
entry1.ts
导入main.ts
导入main.hbs
实际加载main.v1.hbs
main.ts
导入实际加载menu.v1.hbs
的menu.ts
- 等等...
entry2.ts
导入main.ts
实际加载main.v2.hbs
的导入main.hbs
main.ts
导入实际加载menu.v2.hbs
的menu.ts
- 等等...
我认为这种方法会给我带来多种优势:
不必更改所需文件(实际上是数百个)中的源代码。
如果我不关心这个,我可以在表达式中使用 require,然后使用树摇晃只留下每个条目中使用的模板。然而。。。
- 这将增加编译时间和复杂性
- 如果无法正确摇树并且必须将所有变体实际存储在捆绑包中,我可以大大增加捆绑包的大小,以便我可以在运行时访问正确的变体 (eww)
- 我内心的怪胎不太热衷于处理更多东西只是为了把它们扔掉的想法。
- 该方法似乎仅适用于
require()
语法,而不适用于 ES 样式的静态import
- 因此不向前兼容
可以在"重定向"之前检查变体文件是否存在,如果不存在,则回退到默认文件。
重要的是,这允许我逐步完成此转换,因为创建了新"v2"变体的更多文件,而无需保留哪些导入映射到哪些文件或其他文件的静态列表。
也许可以在单个编译运行中执行此转换。
如果我不关心这个,我可以单独编译每个条目,在每次运行时设置适当的选项。但是,该应用程序非常大,需要几分钟才能构建,甚至需要增加节点内存限制。正因为如此,按顺序或并行单独构建每个条目并不是太好,尽管如果没有其他方法,这是我的最后手段。无论如何,考虑到 Webpack 的功能,我觉得这是我应该能够在单个构建中完成的事情。
我尝试了什么
普通模块替换插件
这是 Webpack 内置的插件,乍一看似乎可以满足我的需要:拦截特定模块的require
调用并将它们更改为其他模块,它甚至具有正则表达式和谓词函数支持。但是,由于映射在编译过程中无法更改,我很快就放弃了此操作。这意味着我不能为每个条目设置不同的替换规则。
编写我自己的加载器
现在我们深入战壕。我想,为什么不编写我自己的加载器来解决这个问题呢?加载器能够读取其根条目,因此理论上我应该能够使用该信息来,而不是main.hbs
加载main.v1.hbs
用于entry1
和main.v2.hbs
用于entry2
。
虽然最初这似乎有效(尽管我讨厌这个应用程序的无状态性),但我发现 Webpack 似乎第一次这样做时缓存了 require/Resolution th1e:在处理main.ts
时。因此,即使我拥有所有逻辑,我的加载器每个文件也只调用一次,而不是每个require
一次,我无法实现我想要的。
我研究了告诉 Webpack "不要缓存这个,下次再读一遍"的方法,我没有运气。由于两个条目都导入main.ts
导入main.hbs
一次,因此 Webpack 将 TS 和 HBS 分别视为 1 个模块,而不管它们是否被导入到多个条目文件中。我想这是一个必须的优化,但对于这种特定情况,我没有找到摆脱它的方法。
编写我自己的插件
由于加载器无法满足我的需求,因此我尝试编写插件。我浏览了详细的文档并尝试挂钩compiler
和compilation
但没有走得太远。我浏览了NormalModuleReplacementPlugin
的来源并以相同的方式钩入NormalModuleFactory
(文档似乎没有涵盖它)。最终,通过更改适当资源的请求以包含每个条目的"变体",或多或少地在插件系统中重新实现了我以前的加载器尝试的功能——这正是我所需要的。然而,可悲的是,我遇到了与加载器相同的障碍——文件(和我的代码)只被访问一次。
我还尝试"从外向内"——查看生成的块,其中每个条目在其树中都有 HBS 文件的模块,但这些模块已经被处理和编译,这似乎不是一条成功之路。
编写我自己的决心插件
最后,与常规插件类似,我决定尝试使用解析插件。我一直在想,"我只需要这些require
电话来解决其他地方,这应该不难!
但这也不起作用,有相同的"每个文件只调用一次钩子"的问题,现在我什至无法从所需的文件中获取入口点,因为它们甚至还没有正确解决。
TL;博士
因此,如果我能让我的插件或加载器告诉 Webpack"嘿,下次我require
这个文件时会有所不同,所以请再次检查它",我认为这将解决所有问题并使一切按照我想要的方式工作。
如果这是不可能的,那么我可能会恢复到顺序构建:
- 构建
entry1.js
,*.hbs
决心*.v1.hbs
- 构建
entry2.js
,*.hbs
决心*.v2.hbs
以及刻录时间和内存。但是你能做什么。
感谢您的阅读。
我有同样的问题。唯一对我有帮助的想法是 Webpack 虚拟模块。我根据某些模板动态生成了入口模块的多个实例。因此,这些模块具有不同的 ID,并且在条目之间不共享。