每个条目的 Webpack 模块解析别名规则



这里有一个很长的。尽管我对答案或解决方案的期望不高(或任何东西),但仍然感觉像是一个

有趣的有趣问题,可以分享/发泄。

我有什么

我有一个有 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.hbsmenu.ts
    • 等等...
  • entry2.ts导入main.ts实际加载main.v2.hbs的导入main.hbs
    • main.ts导入实际加载menu.v2.hbsmenu.ts
    • 等等...

我认为这种方法会给我带来多种优势:

  1. 不必更改所需文件(实际上是数百个)中的源代码。

    如果我不关心这个,我可以在表达式中使用 require,然后使用树摇晃只留下每个条目中使用的模板。然而。。。

    • 这将增加编译时间和复杂性
    • 如果无法正确摇树并且必须将所有变体实际存储在捆绑包中,我可以大大增加捆绑包的大小,以便我可以在运行时访问正确的变体 (eww)
    • 我内心的怪胎不太热衷于处理更多东西只是为了把它们扔掉的想法。
    • 该方法似乎仅适用于require()语法,而不适用于 ES 样式的静态import- 因此不向前兼容
  2. 可以在"重定向"之前检查变体文件是否存在,如果不存在,则回退到默认文件。

    重要的是,这允许我逐步完成此转换,因为创建了新"v2"变体的更多文件,而无需保留哪些导入映射到哪些文件或其他文件的静态列表。

  3. 也许可以在单个编译运行中执行此转换。

    如果我不关心这个,我可以单独编译每个条目,在每次运行时设置适当的选项。但是,该应用程序非常大,需要几分钟才能构建,甚至需要增加节点内存限制。正因为如此,按顺序或并行单独构建每个条目并不是太好,尽管如果没有其他方法,这是我的最后手段。无论如何,考虑到 Webpack 的功能,我觉得这是我应该能够在单个构建中完成的事情。

我尝试了什么

普通模块替换插件

这是 Webpack 内置的插件,乍一看似乎可以满足我的需要:拦截特定模块的require调用并将它们更改为其他模块,它甚至具有正则表达式和谓词函数支持。但是,由于映射在编译过程中无法更改,我很快就放弃了此操作。这意味着我不能为每个条目设置不同的替换规则。

编写我自己的加载器

现在我们深入战壕。我想,为什么不编写我自己的加载器来解决这个问题呢?加载器能够读取其根条目,因此理论上我应该能够使用该信息来,而不是main.hbs加载main.v1.hbs用于entry1main.v2.hbs用于entry2

虽然最初这似乎有效(尽管我讨厌这个应用程序的无状态性),但我发现 Webpack 似乎第一次这样做时缓存了 require/Resolution th1e:在处理main.ts时。因此,即使我拥有所有逻辑,我的加载器每个文件也只调用一次,而不是每个require一次,我无法实现我想要的。

我研究了告诉 Webpack "不要缓存这个,下次再读一遍"的方法,我没有运气。由于两个条目都导入main.ts导入main.hbs一次,因此 Webpack 将 TS 和 HBS 分别视为 1 个模块,而不管它们是否被导入到多个条目文件中。我想这是一个必须的优化,但对于这种特定情况,我没有找到摆脱它的方法。

编写我自己的插件

由于加载器无法满足我的需求,因此我尝试编写插件。我浏览了详细的文档并尝试挂钩compilercompilation但没有走得太远。我浏览了NormalModuleReplacementPlugin的来源并以相同的方式钩入NormalModuleFactory(文档似乎没有涵盖它)。最终,通过更改适当资源的请求以包含每个条目的"变体",或多或少地在插件系统中重新实现了我以前的加载器尝试的功能——这正是我所需要的。然而,可悲的是,我遇到了与加载器相同的障碍——文件(和我的代码)只被访问一次。

我还尝试"从外向内"——查看生成的块,其中每个条目在其树中都有 HBS 文件的模块,但这些模块已经被处理和编译,这似乎不是一条成功之路。

编写我自己的决心插件

最后,与常规插件类似,我决定尝试使用解析插件。我一直在想,"我只需要这些require电话来解决其他地方,这应该不难!

但这也不起作用,有相同的"每个文件只调用一次钩子"的问题,现在我什至无法从所需的文件中获取入口点,因为它们甚至还没有正确解决。

TL;博士

因此,如果我能让我的插件或加载器告诉 Webpack"嘿,下次我require这个文件时会有所不同,所以请再次检查它",我认为这将解决所有问题并使一切按照我想要的方式工作。

如果这是不可能的,那么我可能会恢复到顺序构建:

  1. 构建entry1.js*.hbs决心*.v1.hbs
  2. 构建entry2.js*.hbs决心*.v2.hbs

以及刻录时间和内存。但是你能做什么。

感谢您的阅读。

我有同样的问题。唯一对我有帮助的想法是 Webpack 虚拟模块。我根据某些模板动态生成了入口模块的多个实例。因此,这些模块具有不同的 ID,并且在条目之间不共享。

最新更新