【问题标题】:How to share the same instance of jQuery across multiple Webpack packs?如何跨多个 Webpack 包共享同一个 jQuery 实例?
【发布时间】:2020-03-11 21:32:23
【问题描述】:

我正在使用 Rails 应用程序,从 Sprockets 迁移到 Webpack。我们当前的 JavaScript 依赖于全局分配给 window 的库。为了迁移过来,我想保持全局分配,直到我们以后解决它。我想在不更改任何 JavaScript 的情况下从 Sprockets 切换到 Webpack。

我们有几个 JavaScript 包,为了提高性能而手动拆分:

  • core.js,其中包含 jQuery 和其他几个文件。这是一个阻塞包。
  • vendor.js,包含所有其他库和 jQuery 插件。这是一个延迟捆绑包。
  • application.js,其中包含我们的自定义应用程序代码。也是一个延迟捆绑包。

所有的bundle都依赖于jQuery,application.js依赖于vendor.js。这对 Sprockets 来说很好,但对于 Webpack 来说是个问题。

如何将 jQuery 保留在 core.js 中,但将其从 Webpack 中的 vendor.jsapplication.js 中排除? 或另一种询问方式是 我如何共享相同的跨多个 webpack 包的 jQuery 的实例我需要使用同一个实例,因为 application.js 依赖于 vendor.js 中定义的 jQuery 插件

我的配置文件同时包含expose-loaderProvidePlugin,但它们在包本身中包含jQuery,这不是同一个实例。换句话说,我多次捆绑了 jQuery(使用 splitChunks() 修复),但我无法保证我使用的是哪个实例,因此无法保证插件可用。

// Webpacker environment.js config

const { environment } = require('@rails/webpacker');
const path = require('path');
const webpack = require('webpack');

environment.loaders.append('expose', {
  test: require.resolve('jquery'),
  use: [
    {
      loader: 'expose-loader',
      options: 'jQuery'
    },
    {
      loader: 'expose-loader',
      options: '$'
    }
  ]
});

environment.plugins.append(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery'
  })
);


module.exports = environment;

【问题讨论】:

    标签: jquery ruby-on-rails webpack sprockets


    【解决方案1】:

    splitChunks 已成功使用 expose-loader 配置为 jquery combined 强制单个实例,如您所描述的那样。

    这是我正在使用的 Webpack 配置的关键部分:

    environment.splitChunks((config) => {
      return Object.assign({}, config, {
        optimization: {
          splitChunks: {
            chunks: 'all',
            name: true,
            cacheGroups: {
              vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
              },
              default: {
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true,
              },
            },
          },
        },
      })
    })
    

    这里的关键是要理解 Webpack splitChunks 取代了手动代码拆分的需要。换句话说,放弃显式创建core.jsvendor.js 包的需要;将所有导入合并为application.js 依赖树的一部分。 让 Webpack 为你做代码拆分。

    另一个需要注意的是,Webpacker 对splitChunks 的实现意味着在视图中放置一个javascript_packs_with_chunks_tag,这样共享的“运行时”块将确保模块(如 jQuery)不会重复。

    在我之前的 Sprockets 项目中,我曾经像您一样手动拆分捆绑包。当我不得不自己迁移到 Webpack 时,这种新方法需要一段时间才能融入其中。我对此进行了讨论,这将进一步阐明我的答案:https://youtu.be/fKOq5_2qj54?t=185

    【讨论】:

    • 感谢@rossta 的这个和谈话! splitChunks 绝对是我们将来想要使用的东西,但它离我们还有更远的路要走。即便如此,我们可能希望将core.js 保留为自己的包,因为它是唯一的阻塞JS。通过将 defer: true 添加到我们的其他捆绑包中,我们获得了大约 2 秒的性能,我们并不太希望失去它 :) 似乎越来越像我可能在寻找 multiple configurations
    • 另一种看待它的方式,在 Webpack 术语中,你当前包含在 core.js 中的 JS 是你的“初始”块。您可以简单地使用来自初始块的动态导入,而不是使用带有 defer 的单独捆绑包,例如import('../application')。在这种情况下,application.js 不会是一个单独的包,即将它移出app/javascript/packs;相反,它应该是您的 core.js 包的异步依赖项。
    • 换句话说,您的用例正是 splitChunks 的设计目的。您不想制作明确的捆绑包,而是让 Webpack 为您完成工作。
    【解决方案2】:

    在我的案例中唯一有效的两个选项是:

    1) 像这样向外部添加 jquery:

    module.exports = {
        ...
        externals: {
        'jquery': 'jQuery'
        },
    };
    

    将脚本标记放入您在任何地方都包含的文件中(在我的例子中是 base.html.twig,其他所有内容都由此扩展)。

    <script src="/path/to/jquery/jquery-2.2.3.min.js"></script>
    

    2) 使用ProvidePlugin + runtimeChunk: single:

    module.exports = {
        ...
        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery',
                "window.jQuery": 'jquery',
                "window.$": 'jquery',
            })
        ],
        optimization: {
            runtimeChunk: 'single',
        }
    }
    

    runtimeChunk single 将生成一个新文件,您必须将其包含在一个页面中,该页面无处不在(基本上无论您有一个入口点,您都包含此运行时块!)

    【讨论】:

      【解决方案3】:

      虽然我还没有解决方案,但我发现将 jQuery 设置为外部库似乎会强制使用单个全局实例。

      // Webpacker environment.js config
      
      const { environment } = require('@rails/webpacker');
      const path = require('path');
      const webpack = require('webpack');
      
      environment.config.set('externals', {
        $: 'jquery',
        jquery: 'jQuery'
      });
      
      module.exports = environment;
      

      我已经能够通过使用我的 Sprockets core.js 捆绑包和 Webpack 来测试这一点。这一切都按预期工作。

      我已经发布了一个后续问题,试图有条件地设置外部并将 Webpack 用于所有内容:Conditionally set externals for a specific Webpack bundle

      【讨论】:

        【解决方案4】:

        不确定这是否可行,但也许您可以尝试:

        # vendor.js
        
        import JQuery from 'jquery' 
        window.$ = window.JQuery = JQuery
        
        // rest of vendor.js code
        
        
        #application.js
        import './vendor.js'
        
        //rest of application.js code
        

        【讨论】:

        • 似乎无法让它工作。 jQuery 包含在两个包中,并且实例不同?‍♂️
        猜你喜欢
        • 2023-04-02
        • 1970-01-01
        • 1970-01-01
        • 2022-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-27
        • 1970-01-01
        相关资源
        最近更新 更多