【问题标题】:Webpack per-entry module resolve aliasing rulesWebpack per-entry 模块解析别名规则
【发布时间】:2019-10-11 02:56:14
【问题描述】:

这里有一个很长的。尽管我对答案或解决方案的期望不高(或任何东西),但我仍然觉得分享/发泄是一个有趣有趣的问题。

我有什么

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

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

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

    如果我不关心这个,我可以使用require with expression,然后使用摇树来只留下每个条目中使用的模板。不过……

    • 这会增加编译时间和复杂度
    • 如果不能正确地进行 tree-shake 并且必须将所有变体实际存储在一个包中,这样我就可以在运行时访问正确的变体 (eww),可能会大大增加包的大小
    • 我这个整洁的怪胎不太热衷于处理更多东西只是为了扔掉它们的想法
    • 该方法似乎只适用于 require() 语法,而不适用于 ES 风格的静态 import -- 所以不向前兼容
  2. 甚至可以在“重定向”之前检查变体文件是否存在,如果不存在则回退到默认文件。

    重要的是,随着新的“v2”变体的更多文件的创建,这允许我逐步执行此转换,而无需保留哪些导入映射到哪些文件或其他内容的静态列表。

  3. 也许可以在一次编译运行中进行此转换。

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

我尝试了什么

NormalModuleReplacementPlugin

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

Writing my own Loader

现在我们深陷其中。我想,为什么不写我自己的加载器来解决这个问题呢?加载器is capable 读取其根条目,因此理论上我应该能够使用该信息来代替main.hbs 加载main.v1.hbsentry1main.v2.hbsentry2

虽然最初这似乎可行(尽管我讨厌这种方法的非无状态性),但我发现 Webpack 似乎在第一次这样做时缓存了要求/分辨率:在处理 main.ts 时。所以即使我有所有的逻辑,我的加载器每个文件只调用一次,而不是每个require 一次,我无法实现我想要的。

我研究了告诉 Webpack“不要缓存这个,下次再读”的方法,我没有运气。由于两个条目都导入了main.ts,而main.hbs 只导入了一次,因此Webpack 将TS 和HBS 都视为一个模块,而不管它们被导入多个条目文件的事实。我想这是一个必须进行的优化,但对于这种特定情况,我没有找到解决办法。

Writing my own Plugin

由于加载器没有得到我需要的东西,我尝试编写一个插件。我浏览了详细的文档并尝试连接到compilercompilation,但并没有走得太远。我查看了NormalModuleReplacementPluginsource 并以同样的方式连接到NormalModuleFactory(文档似乎没有涵盖它)。最终通过更改适当资源的请求以包含每个条目的“变体”,或多或少地重新实现了我之前在插件系统中尝试的加载器的功能——这正是我所需要的。然而,遗憾的是,我遇到了与加载程序相同的障碍——文件(和我的代码)只被访问一次。

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

Writing my own resolve Plugin

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

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

TL;DR

所以如果我可以让我的插件或加载器告诉 Webpack “嘿,下次我require 时这个文件会有所不同,所以请再检查一次”,我认为这将解决所有问题并使一切正常工作我想要它。

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

  1. 构建entry1.js*.hbs解析为*.v1.hbs
  2. 构建entry2.js*.hbs 解析为*.v2.hbs

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

感谢您的阅读。

【问题讨论】:

  • 您找到解决方案了吗?我有类似的问题stackoverflow.com/questions/56691089/… 在github上:github.com/webpack/enhanced-resolve/issues/180
  • @webdeb 在某种程度上,我做到了。我放弃了将所有东西作为一个整体构建,最终生成了 2 个具有相同共享选项的单独配置对象。对于每个配置,我为内置的NormalModuleReplacementPlugin 设置了特定选项(实际上编写了一个自定义插件来包装NormalModuleReplacementPlugin),所以现在每个条目都有不同的模块。然后我将这两种配置组合成一个数组(MultiConfiguration),将其提供给 Webpack。不像我想要的那样优化,但它可以工作并且比单独编译的循环更好。
  • 好的,我认为这也是建议的选项。 github.com/webpack/webpack/tree/master/examples/…

标签: webpack


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 2018-09-21
    • 2017-08-07
    • 2017-07-27
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 2020-11-13
    相关资源
    最近更新 更多