Verwalten der Abhängigkeit von jQuery-Plugins im Webpack


451

Ich verwende Webpack in meiner Anwendung, in der ich zwei Einstiegspunkte erstelle: bundle.js für alle meine JavaScript-Dateien / -Codes und vendor.js für alle Bibliotheken wie jQuery und React. Was mache ich, um Plugins zu verwenden, deren Abhängigkeiten jQuery sind, und ich möchte sie auch in vendor.js haben? Was ist, wenn diese Plugins mehrere Abhängigkeiten haben?

Derzeit versuche ich, dieses jQuery-Plugin hier zu verwenden - https://github.com/mbklein/jquery-elastic . In der Webpack-Dokumentation werden ProvidePlugin und Imports-Loader erwähnt. Ich habe requirePlugin verwendet, aber das jQuery-Objekt ist immer noch nicht verfügbar. So sieht meine webpack.config.js aus:

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;

Trotzdem wird immer noch ein Fehler in der Browserkonsole ausgegeben:

Nicht erfasster ReferenceError: jQuery ist nicht definiert

Wenn ich den Import-Loader verwende, wird ebenfalls ein Fehler ausgegeben:

erfordern ist nicht definiert '

in dieser Zeile:

var jQuery = require("jquery")

Ich könnte jedoch dasselbe Plugin verwenden, wenn ich es nicht zu meiner Datei vendor.js hinzufüge und es stattdessen auf normale AMD-Weise benötige, wie ich meine anderen JavaScript-Codedateien einbinde, wie z.

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

});

Dies ist jedoch nicht das, was ich tun möchte, da dies bedeuten würde, dass jquery.elastic.source.js zusammen mit meinem JavaScript-Code in bundle.js gebündelt wird und ich möchte, dass alle meine jQuery-Plugins im Bundles vendor.js enthalten sind. Wie gehe ich vor, um dies zu erreichen?


3
Ich bin mir nicht sicher, ob dies Ihr Problem ist, aber Sie müssen windows.jQuery definitiv in "window.jQuery" ändern: "jquery". Es gibt einen Tippfehler auf der Webpack-Website, von dem ich annehme, dass Sie diesen Code erhalten haben.
Alex Hawkins

@AlexHawkins Oh ja, das habe ich bemerkt und behoben. Vielen Dank für den Hinweis!
Booleanhunter

Antworten:


770

Sie haben verschiedene Ansätze zum Einbeziehen älterer Anbietermodule gemischt. So würde ich es angehen:

1. Bevorzugen Sie nicht minimiertes CommonJS / AMD gegenüber dist

Die meisten Module verknüpfen die distVersion im mainBereich ihrer package.json. Während dies für die meisten Entwickler nützlich ist, ist es für Webpack besser, die srcVersion zu aliasen, da Webpack auf diese Weise Abhängigkeiten besser optimieren kann (z. B. bei Verwendung von DedupePlugin).

// webpack.config.js

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

In den meisten Fällen distfunktioniert die Version jedoch auch einwandfrei.


2. Verwenden Sie die ProvidePlugin, um implizite Globals zu injizieren

Die meisten Legacy-Module basieren auf dem Vorhandensein bestimmter globaler Module, wie dies bei jQuery-Plugins auf $oder der Fall ist jQuery. In diesem Szenario können Sie das Webpack so konfigurieren, var $ = require("jquery")dass es jedes Mal vorangestellt wird, wenn es auf die globale $Kennung trifft .

var webpack = require("webpack");

    ...

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

3. Konfigurieren Sie mit dem Import-Loaderthis

Einige ältere Module sind darauf angewiesen this, das windowObjekt zu sein. Dies wird zu einem Problem, wenn das Modul in einem CommonJS-Kontext ausgeführt wird, in dem thisgleich ist module.exports. In diesem Fall können Sie thismit dem Import-Loader überschreiben .

Lauf npm i imports-loader --save-devund dann

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

Der Import-Loader kann auch zum manuellen Einfügen von Variablen aller Art verwendet werden. Aber meistens ProvidePluginist das nützlicher, wenn es um implizite Globale geht.


4. Deaktivieren Sie AMD mit dem Import-Loader

Es gibt Module, die verschiedene Modulstile unterstützen, wie AMD, CommonJS und Legacy. Meistens suchen sie jedoch zuerst nach defineetwas eigenartigem Code, um Eigenschaften zu exportieren, und verwenden ihn dann. In diesen Fällen kann es hilfreich sein, den CommonJS-Pfad durch Festlegen zu erzwingen define = false.

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

5. Verwenden Sie den Script-Loader , um Skripte global zu importieren

Wenn Sie sich nicht für globale Variablen interessieren und nur möchten, dass ältere Skripte funktionieren, können Sie auch den Script-Loader verwenden. Es führt das Modul in einem globalen Kontext aus, als hätten Sie es über das <script>Tag eingefügt.


6. Verwenden Sie noParsediese Option, um große Entfernungen einzuschließen

Wenn es keine AMD / CommonJS-Version des Moduls gibt und Sie die einschließen möchten dist, können Sie dieses Modul als kennzeichnen noParse. Dann enthält das Webpack nur das Modul, ohne es zu analysieren, wodurch die Erstellungszeit verbessert werden kann. Dies bedeutet, dass Funktionen, für die AST erforderlich ist , wie z. B. die ProvidePlugin, nicht funktionieren.

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

3
Das ProvidePluginwird auf alle Vorkommen der angegebenen Bezeichner in allen Dateien angewendet. Das imports-loaderkann nur auf bestimmte Dateien angewendet werden, Sie sollten jedoch nicht beide für dieselben Variablen / Abhängigkeiten verwenden.
Johannes Ewald

5
Das verwirrt mich. Woher kommt die Abfrage eigentlich? Vor Ort oder ein CDN? Alles nach 3. ist unklar, ob das notwendig ist. Was sind die eigentlichen Schritte, um jquery mithilfe von Webpack in Ihr Projekt zu integrieren? Umgeht es die Version, die über CDN verfügbar ist?
HelpMeStackOverflowMyOnlyHope

2
Alles von einem CDN ist für das Webpack nicht verfügbar, daher bezieht sich dies auf eine lokale JQuery-Installation. Jquery kann nur erforderlich sein, es sind keine besonderen Schritte erforderlich, um es zu integrieren. In dieser Frage geht es darum, wie JQuery-Plugins integriert werden, die von der globalen $Variablen abhängen .
Johannes Ewald

8
Hat das alles getan, immer noch nicht funktioniert; Dann fügte hinzu: "module": { "loaders": [ { test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },und es hat gut funktioniert.
Musikformellons

5
Tun Sie das alles, um nur ein Abfragepaket einzuschließen? Was für ein Schmerz. Ich gehe zurück zu meinem Schluckaufbau
Rahul Gupta

89

Für den globalen Zugriff auf jquery stehen mehrere Optionen zur Verfügung. In meinem letzten Webpack-Projekt wollte ich globalen Zugriff auf jquery, daher habe ich meinen Plugins-Deklarationen Folgendes hinzugefügt:

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

Dies bedeutet dann, dass auf jquery aus dem JavaScript-Quellcode über die globalen Referenzen $ und jQuery zugegriffen werden kann.

Natürlich müssen Sie jquery auch über npm installiert haben:

$ npm i jquery --save

Ein funktionierendes Beispiel für diesen Ansatz finden Sie in meiner App auf Github


2
Bitte können Sie einen Kommentar hinterlassen, wenn Sie eine Abstimmung mit Begründung herabstufen. Die obigen Informationen sind korrekt. Weitere Informationen finden Sie in meiner Arbeits-App unter: github.com/arcseldon/react-babel-webpack-starter-app. Verwenden Sie dazu den oben beschriebenen Ansatz.
Arcseldon

Ich bin nicht der Downvoter, aber vielleicht liegt das daran, dass die von ProvidePluginIhnen vorgeschlagene Lösung bereits vorgeschlagen wurde?
Léo Lam

@ LéoLam - danke für deinen Kommentar. Schätzen Sie Ihren Standpunkt - ich hatte die Antwort über separate Anfragen abgeleitet und nur den Ausschnitt hier geteilt, von dem ich dachte, dass er für andere, die nachahmen möchten, was ich getan habe, wahrscheinlich am relevantesten ist. Sie haben Recht, die offizielle Antwort deckt diese Option ab.
Arcseldon

1
Es funktioniert, aber ich bekomme 2 Warnungen: ./~/jQuery/dist/jquery.js There is another module with an equal name when case is ignored.und die gleiche mit./~/jquery/dist/jquery.js
Pistou

7
Ich musste 'window.jQuery': 'jquery'dieser Liste hinzufügen , damit das Paket ms-signalr-client npm geladen wurde. Ich habe auch 'window.$': 'jquery'für ein gutes Maß eingesetzt :)
Sammi

57

Ich weiß nicht, ob ich sehr gut verstehe, was Sie versuchen, aber ich musste jQuery-Plugins verwenden, für die jQuery im globalen Kontext (Fenster) sein musste, und ich habe Folgendes in mein entry.js:

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

Das muss ich nur benötigen wo immer ich will jqueryplugin.min.jsund window.$wird wie erwartet mit dem Plugin erweitert.


2
Grundsätzlich habe ich den Fehler gemacht, das ProvidePlugin zusammen mit der noParse-Bedingung zu verwenden. Plugins wie ProvidePlugin funktionieren nicht, wenn wir NoParse ausführen, wie in Punkt 6 der Antwort angegeben. Sie können diesen Fehler im Code sehen
booleanhunter

Ja, ich habe festgestellt, dass Plugins konsistenter funktionieren als die bereitgestellten Plugins usw., insbesondere wenn Sie Angular 1.x über den Script-Loader einspielen.
Tracker1

27

Ich habe Dinge gut arbeiten , während auszusetzen $und jQueryals globale Variablen mit Webpack 3.8.1 und den folgenden.

Installieren Sie jQuery als Projektabhängigkeit. Sie können die @3.2.1Installation der neuesten Version unterlassen oder eine andere Version angeben.

npm install --save jquery@3.2.1

Installieren Sie expose-loaderals Entwicklungsabhängigkeit , wenn nicht bereits installiert ist .

npm install expose-loader --save-dev

Konfigurieren Sie Webpack so, dass jQuery für uns geladen und verfügbar gemacht wird.

// 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'
    })
  ]
}

5
Sie haben vergessen zu erwähnen, dass Sie installieren expose-loadermüssen, damit es richtig funktioniert npm install expose-loader --save-dev
Paulo Griiettner

4
Das hat bei mir funktioniert 👍. JQuery: 3.3.1, Expose-Loader: 0.7.5, Webpack: 4.20.2
AKT

Der expose-loadedhat es für mich getan. Ich habe beim Kompilieren keinen Fehler erhalten, aber in Chrome-Entwicklertools. Das ist jetzt gelöst.
Johan Vergeer

Vielen Dank! Dies funktionierte mit "jquery": "^ 3.4.1" und "webpack": "^ 4.41.5". Ist es nur ich oder sollte es nicht so lächerlich sein, jQuery dazu zu bringen, mit Webpack zu arbeiten?
Carles Alcolea

18

Fügen Sie dies Ihrem Plugins-Array in webpack.config.js hinzu

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

dann benötigen Sie normalerweise jquery

require('jquery');

Wenn der Schmerz weiterhin besteht, dass andere Skripte es sehen, versuchen Sie, es explizit über (im Eintrag js) in den globalen Kontext zu stellen.

window.$ = jQuery;

18

Fügen Sie in Ihrer Datei webpack.config.js Folgendes hinzu:

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

Installieren Sie jQuery mit npm:

$ npm i jquery --save

Fügen Sie in der Datei app.js die folgenden Zeilen hinzu:

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

Das hat bei mir funktioniert. :) :)


Wie wird sich das verhalten, wenn Sie zum Beispiel haben. app.js und jquery-module.js, die beide jquery benötigen? Für mich bekomme ich jquery13981378127 und jquery12389723198 als Instanzen im Fenster?
Martea

8

Ich habe einige der Antworten ausprobiert, aber keine schien zu funktionieren. Dann habe ich das versucht:

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

Scheint zu funktionieren, egal welche Version ich benutze


+1 scheint ein Missverständnis zu sein, dass hier hinzugefügte Globals automatisch gegen das Fenster gesetzt werden. Es scheint nicht so zu funktionieren, dass Sie explizit sein müssen.
James

Richtig, dies fügt es nicht zum Fensterobjekt hinzu. Es erstellt einen Fensteralias innerhalb des Webpacks. Wenn Sie also versuchen, eine $außerhalb eines Webpacks erforderliche Datei zu verwenden, ist diese undefiniert.
Cam Tullos

7

Dies funktioniert in Webpack 3:

in der Datei webpack.config.babel.js:

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

Und verwenden Sie ProvidePlugin

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

1
Für andere, die bei Webpack super neu sind (wie ich!), Geht "Entschlossenheit" in Ihre webpack.config.js unter module.exports = {}, genau wie Eintrag, Ausgabe, Plugins, Modul usw.
DavGarcia

1
Und neues webpack.ProvidePlugin geht in das Plugins-Array.
DavGarcia

2

Die beste Lösung, die ich gefunden habe, war:

https://github.com/angular/angular-cli/issues/5139#issuecomment-283634059

Grundsätzlich müssen Sie eine Dummy-Variable in typings.d.ts einfügen, alle "import * as $ from 'jquery" aus Ihrem Code entfernen und dann manuell ein Tag zum jQuery-Skript zu Ihrem SPA-HTML hinzufügen. Auf diese Weise steht Ihnen das Webpack nicht im Weg, und Sie sollten in allen Ihren Skripten auf dieselbe globale jQuery-Variable zugreifen können.


2

Dies funktioniert bei mir in der Datei webpack.config.js

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

in einem anderen Javascript oder in HTML hinzufügen:

global.jQuery = require('jquery');

0

Bearbeiten: Manchmal möchten Sie Webpack einfach als Modulbündler für ein einfaches Webprojekt verwenden, um Ihren eigenen Code zu organisieren. Die folgende Lösung ist für diejenigen gedacht, die nur möchten, dass eine externe Bibliothek in ihren Modulen wie erwartet funktioniert - ohne viel Zeit in Webpack-Setups zu investieren. (Bearbeitet nach -1)

Schnelle und einfache (es6) Lösung, wenn Sie immer noch Probleme haben oder die externe Konfiguration / zusätzliche Webpack-Plugin-Konfiguration vermeiden möchten:

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

innerhalb eines Moduls:

const { jQuery: $, Underscore: _, etc } = window;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.