如何在多个Webpack包之间共享同一个jQuery实例?

3
我正在开发一个Rails应用程序,从Sprockets迁移到Webpack。我们当前的JavaScript依赖于全局分配在window上的库。为了迁移,我想保持全局分配,直到我们稍后再处理它。我想在没有任何JavaScript更改的情况下从Sprockets切换到Webpack。
我们有几个JavaScript包,为了提高性能,它们已经被手动拆分:
  • core.js,其中包含jQuery和其他一些文件。这是一个阻塞包。
  • vendor.js,其中包含所有其他库和jQuery插件。这是一个延迟包。
  • application.js,其中包含我们自定义的应用程序代码。也是一个延迟包。
所有的包都依赖于jQuery,而application.js依赖于vendor.js。这对于Sprockets来说很好,但在Webpack中会出现问题。 如何在Webpack中将jQuery保留在core.js中,但将其从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;
4个回答

4
我已经成功使用了splitChunks来强制使jquery的单个实例与您描述的expose-loader配置相结合。
这是我正在使用的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绝对是我们未来想要使用的东西,但这还需要一些时间。即使如此,由于它是唯一的阻塞JS,我们可能仍然希望将core.js作为自己的捆绑包。通过将defer:true添加到其他捆绑包中,我们大约获得了2秒的性能提升,我们不太愿意失去它 :)看起来我可能正在寻找多个配置 - bholtbholt
1
另一种理解方式,在Webpack术语中,你当前包含在 core.js 中的JS是你的“initial” chunk。不用一个带有延迟属性 defer 的单独打包文件来存储其余部分,你可以从初始chunk中简单地使用动态导入,例如 import('../application')。在这种情况下,application.js 将不是一个独立的打包文件,即将其移出 app/javascript/packs 文件夹;它应该成为你的 core.js 打包文件的异步依赖项。 - rossta
换句话说,您的用例正是 splitChunks 设计的目的。您不想制作明确的捆绑包,而是让Webpack为您完成工作。 - rossta

1
我的情况下,只有两个选项是有效的:
1)像这样将jquery添加到外部文件中:
module.exports = {
    ...
    externals: {
    'jquery': 'jQuery'
    },
};

在一个你随处包含的文件中放置一个脚本标签(在我的情况下是base.html.twig,其他所有内容都从这个文件扩展)。
<script src="/path/to/jquery/jquery-2.2.3.min.js"></script>

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

runtimeChunk single会生成一个新文件,您需要在包含每个入口点的页面中包含它(基本上在您有入口点的任何地方都要包含此运行时块!)


1
非常感谢,我已经搜索了两天,只有你的解决方案帮助了我))) - askrynnikov

1
虽然我还没有解决方案,但我发现将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用于所有内容:为特定的Webpack捆绑包有条件地设置外部依赖项


0

我不太确定这会不会有效,但或许你可以尝试一下:

# 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 包含在两个捆绑包中,而实例是不同的 ‍♂️ - bholtbholt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接