在webpack中管理jQuery插件依赖

499

我在我的应用程序中使用Webpack,其中我创建了两个入口点- bundle.js 用于所有JavaScript文件/代码,vendors.js 用于像jQuery和React这样的所有库。如果我想要使用具有 jQuery 依赖关系的插件,并且我还希望将它们放在 vendors.js 中,该怎么做?如果这些插件有多个依赖项会怎么样?

目前我正在尝试使用这个jQuery插件- https://github.com/mbklein/jquery-elastic。Webpack文档提到了providePlugin和imports-loader。我使用了providePlugin,但仍然无法使用jQuery对象。下面是我的webpack.config.js的样子-

var webpack = require('webpack');
var bower_dir = __dirname + '/bower_components';
var node_dir = __dirname + '/node_modules';
var lib_dir = __dirname + '/public/js/libs';

var config = {
    addVendor: function (name, path) {
        this.resolve.alias[name] = path;
        this.module.noParse.push(new RegExp(path));
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jquery: "jQuery",
            "window.jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js', Infinity)
    ],
    entry: {
        app: ['./public/js/main.js'],
        vendors: ['react','jquery']
    },
    resolve: {
        alias: {
            'jquery': node_dir + '/jquery/dist/jquery.js',
            'jquery.elastic': lib_dir + '/jquery.elastic.source.js'
        }
    },
    output: {
        path: './public/js',
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            { test: /\.js$/, loader: 'jsx-loader' },
            { test: /\.jquery.elastic.js$/, loader: 'imports-loader' }
        ]
    }
};
config.addVendor('react', bower_dir + '/react/react.min.js');
config.addVendor('jquery', node_dir + '/jquery/dist/jquery.js');
config.addVendor('jquery.elastic', lib_dir +'/jquery.elastic.source.js');

module.exports = config;

但是尽管如此,在浏览器控制台上仍然会出现错误:

Uncaught ReferenceError: jQuery没有定义

同样地,当我使用imports-loader时,它会在这一行中报错:

require没有定义

请注意,此处保留了HTML标签。

var jQuery = require("jquery")

然而,如果我不将插件添加到我的vendors.js文件中,而是按照常规的AMD方式要求它,就像我包含其他JavaScript代码文件一样,我仍然可以使用同样的插件,如:

define(
[
    'jquery',
    'react',
    '../../common-functions',
    '../../libs/jquery.elastic.source'
],function($,React,commonFunctions){
    $("#myInput").elastic() //It works

});

但这不是我想要做的,因为这意味着jquery.elastic.source.js会被捆绑到我的JavaScript代码中成为bundle.js,而我希望所有的jQuery插件都在vendors.js捆绑包中。那么我该如何实现这一点?

12个回答

829

你混合了不同的方法来包含旧版供应商模块。这是我处理它的方式:

1. 优先使用未压缩的CommonJS/AMD而非 dist

大多数模块在其package.jsonmain字段中链接dist版本。虽然对于大多数开发者来说这很有用,但对于webpack来说,最好别名为src版本,因为这样webpack能更好地优化依赖项(例如当使用DedupePlugin时)。

// webpack.config.js

module.exports = {
    ...
    resolve: {
        alias: {
            jquery: "jquery/src/jquery"
        }
    }
};

然而,在大多数情况下,dist 版本也能正常工作。


2. 使用 ProvidePlugin 注入隐式全局变量

大多数旧版模块依赖于特定的全局变量,例如 jQuery 插件依赖于 $jQuery。在这种情况下,你可以配置 webpack,每次遇到全局变量 $ 标识符时,都添加 var $ = require("jquery")

var webpack = require("webpack");

    ...
    
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ]

3. 使用 imports-loader 配置 this

一些旧模块依赖于 thiswindow 对象。当模块在 CommonJS 环境中执行时,this 等于 module.exports,这将成为一个问题。在这种情况下,您可以使用 imports-loader 来覆盖 this

运行 npm i imports-loader --save-dev,然后:

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?this=>window"
        }
    ]
}

使用imports-loader也可以手动注入各种变量。但大多数情况下,当涉及到隐式全局变量时,ProvidePlugin更有用。


4. 使用imports-loader禁用AMD

有些模块支持不同的模块风格,如AMD、CommonJS和遗留模块风格。然而,大多数情况下,它们首先检查define,然后使用一些奇怪的代码来导出属性。在这些情况下,通过设置define = false强制使用CommonJS路径可能会有所帮助。

module: {
    loaders: [
        {
            test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
            loader: "imports-loader?define=>false"
        }
    ]
}

5. 使用 script-loader(不再维护)来全局导入脚本

如果您不关心全局变量,只想使用旧版脚本,则可以使用 script-loader。它在全局上下文中执行模块,就像您通过 <script> 标签包含它们一样。


6. 使用 noParse 来包含大型库

当模块不存在 AMD/CommonJS 版本且您要包含 dist 时,可以将此模块标记为 noParse。这样,webpack 将仅包含该模块而不解析它,可用于提高构建时间。这意味着任何需要 AST 的功能,例如 ProvidePlugin,将无法工作。

module: {
    noParse: [
        /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
    ]
}

3
ProvidePlugin会在所有文件中应用给定标识符的所有出现情况。 imports-loader只能在特定文件上应用,但是对于相同的变量/依赖项,不应同时使用两者。 - Johannes Ewald
7
我对此感到困惑。JQuery实际上是从哪里来的?是本地还是CDN?在版本号3之后的所有内容都不太清楚是否必要。使用webpack将jquery集成到您的项目中的实际步骤是什么?它是否绕过了CDN可用的版本? - HelpMeStackOverflowMyOnlyHope
4
CDN提供的内容超出了webpack的范围,所以这指的是本地安装的jquery。可以通过require来获取jquery,集成它没有特殊的步骤。这个问题是关于如何集成依赖于全局变量“$”的jquery插件的。 - Johannes Ewald
8
做了所有这些,仍然不起作用;然后添加了以下内容:"module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" }, 然后它就正常工作了。 - musicformellons
12
只是为了引入一个jquery包就要做这么多事情吗?真是太麻烦了。我要回去用我的gulp构建了。 - Rahul Gupta
显示剩余21条评论

97

要在全局范围内访问jQuery有几个选项。在我最近的webpack项目中,我想要全局访问jQuery,所以我将以下内容添加到我的插件声明中:

 plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
  ]

иҝҷж„Ҹе‘ізқҖйҖҡиҝҮе…ЁеұҖеј•з”Ё$е’ҢjQueryпјҢеҸҜд»Ҙд»ҺJavaScriptжәҗд»Јз Ғдёӯи®ҝй—®jqueryгҖӮ

еҪ“然пјҢжӮЁиҝҳйңҖиҰҒйҖҡиҝҮnpmе®үиЈ…jqueryпјҡ

$ npm i jquery --save

如果您想运用这种方法的话,可以随意在Github上Fork我的应用程序以获取一个可行的示例。


2
如果您降低投票,请解释原因并留下评论。以上信息准确无误。请使用上述方法查看我的工作应用程序:https://github.com/arcseldon/react-babel-webpack-starter-app。 - arcseldon
我不是点踩者,但也许这是因为你建议的 ProvidePlugin 解决方案已经被提出了? - Léo Lam
1
它可以工作,但我收到了2个警告:./~/jQuery/dist/jquery.js 当忽略大小写时,存在另一个具有相同名称的模块。./~/jquery/dist/jquery.js也是一样的。 - pistou
8
我需要在列表中添加 'window.jQuery': 'jquery' 才能使 ms-signalr-client npm 包成功加载。我还为了保险起见加入了 'window.$': 'jquery' :) - Sámal Rasmussen
我不明白在哪里放置 plugins:[...] 代码片段。 - Omar
显示剩余4条评论

62

我不太确定我是否很好地理解了你想要做的事情,但我必须使用需要将jQuery放在全局上下文(window)中的jQuery插件,并且我将以下内容放在我的entry.js中:

var $ = require('jquery');
window.jQuery = $;
window.$ = $;

我只需要在想要使用 jqueryplugin.min.js 的地方引入它,这样就可以按预期扩展 window.$


2
基本上,我犯了一个错误,就是在使用noParse条件时同时使用ProvidePlugin。像ProvidePlugin这样的插件如果我们使用NoParse就无法工作,正如答案中第6点所述。您可以在代码中看到这个错误。 - booleanhunter
是的,我发现这比提供插件等在各种插件中更一致地工作... 特别是如果你通过脚本加载器引入 Angular 1.x。 - Tracker1

34

使用Webpack 3.8.1和以下步骤,我成功地将$jQuery暴露为全局变量。

将jQuery安装为项目依赖项。您可以省略@3.2.1以安装最新版本或指定其他版本。

npm install --save jquery@3.2.1
安装expose-loader作为开发依赖项,如果尚未安装。
npm install expose-loader --save-dev

配置 Webpack 加载并暴露 jQuery 给我们使用。

// webpack.config.js
const webpack = require('webpack')

module.exports = {
  entry: [
    // entry bits
  ],
  output: {
    // output bits
  },
  module: {
    rules: [
      // any other rules
      {
        // Exposes jQuery for use outside Webpack build
        test: require.resolve('jquery'),
        use: [{
          loader: 'expose-loader',
          options: 'jQuery'
        },{
          loader: 'expose-loader',
          options: '$'
        }]
      }
    ]
  },
  plugins: [
    // Provides jQuery for other JS bundled with Webpack
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}

8
您忘了提到需要安装 expose-loader 才能使它正常工作。 npm install expose-loader --save-dev - Paulo Griiettner
expose-loaded 对我很有帮助。我在编译时没有收到错误信息,但在 Chrome 开发者工具中出现了问题。现在问题已经解决了。 - Johan Vergeer
3
对我来说,使用稍微不同的语法也能够实现:test: require.resolve("jquery"), loader: "expose-loader", options: { exposes: ["$", "jQuery"], }, }, - iBobb

32

在您的 webpack.config.js 文件中添加以下内容:

 var webpack = require("webpack");
 plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
 ],

使用npm安装jQuery:

$ npm i jquery --save

在app.js文件中添加以下行:

import $ from 'jquery';
window.jQuery = $;
window.$ = $;

这个对我有用。:)


1
如果你有一个app.js和jquery-module.js,两者都需要jquery,那么它会如何运作?对我来说,窗口上会出现jquery13981378127和jquery12389723198这两个实例。 - Martea
1
注意在 "jquery" 中的大小写敏感性。在我的webpack.config外部,我有 "jquery:'jQuery'"。在我的脚本中,我有 "import $ from 'jQuery'"。更改为 "import $ from 'jquery'" 解决了我的问题 :) - Jono

18

将以下代码添加到你的webpack.config.js文件的插件数组中:

new webpack.ProvidePlugin({
    'window.jQuery': 'jquery',
    'window.$': 'jquery',
})

然后正常地要求jQuery

require('jquery');
如果疼痛持续,请尝试通过以下方式将其显式地放置在全局上下文中(在入口js中):(获取其他脚本以查看它)。
window.$ = jQuery;

11

我尝试了一些提供的答案,但似乎都不起作用。然后我尝试了这个:

new webpack.ProvidePlugin({
    'window.jQuery'    : 'jquery',
    'window.$'         : 'jquery',
    'jQuery'           : 'jquery',
    '$'                : 'jquery'
});

无论我使用哪个版本,似乎都能正常工作。


+1似乎是一个误解,即在此处添加的全局变量将自动设置为窗口,但似乎并不像这样工作,您需要明确说明。 - James
1
正确的做法是,这不会将它添加到窗口对象中。它在webpack内部创建了一个窗口别名。因此,如果您尝试在webpack所需的文件之外使用 $,它将是未定义的。 - Cam Tullos

7

这在webpack 3中有效:

在webpack.config.babel.js文件中:

resolve: {
    alias: {
         jquery: "jquery/src/jquery"
    },
 ....
}

使用 ProvidePlugin

new webpack.ProvidePlugin({
        '$': 'jquery',
        'jQuery': 'jquery',
    })

2
对于那些像我一样对webpack非常陌生的人,“resolve”应该放在你的webpack.config.js文件中的module.exports = {}下面,就像entry、output、plugins、module等一样。 - DavGarcia
2
新的webpack.ProvidePlugin放在插件数组中。 - DavGarcia

3

编辑:有时您只想将Webpack用作简单Web项目的模块打包程序-以保持自己的代码组织。以下解决方案适用于那些只想使外部库在其模块内按预期工作而不使用大量时间潜入Webpack设置的人。(在-1之后编辑)

如果您仍然苦苦挣扎或想避免外部配置/额外的Webpack插件配置,则可以使用快速简单(es6)解决方案:

<script src="cdn/jquery.js"></script>
<script src="cdn/underscore.js"></script>
<script src="etc.js"></script>
<script src="bundle.js"></script>

在一个模块内:

const { jQuery: $, Underscore: _, etc } = window;

2
这对我来说在webpack.config.js上有效。
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery'
    }),

在另一个 JavaScript 文件或 HTML 中添加以下内容:
global.jQuery = require('jquery');

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