Webpack: Wie kann jQuery automatisch erkannt und als angle.element anstelle von jqLite verwendet werden?

Lesezeit: 11 Minuten

Benutzer-Avatar
Boris Burkow

Ich verwende Webpack, um ein Angular 1.4-Projekt zu erstellen. Das Projekt verwendet mehrere jQuery-Plug-ins, die in Winkelanweisungen verpackt sind. Diese Direktiven werden intern verwendet angular.elementwas wahrscheinlich bedeutet, dass angle.element die echte jQuery ist, nicht jqLite.

Ich möchte, dass Winkel jQuery automatisch erkennt und anstelle von jqLite verwendet. Ich habe versucht, jquery lokal in meinem Einstiegspunktmodul anzufordern app.js: require('jquery') und um jQuery global verfügbar zu machen require(expose?$!expose?jQuery!jquery).

Trotzdem, was immer ich tue, angular.element bezieht sich auf jqLite.


Meine Recherche führte zu mehreren Erkenntnissen:

  1. Auch wenn es als CommonJS-Modul importiert wird, weist sich Angular selbst einer globalen Variablen zu window.angularalso brauche ich das nicht expose it with Webpack: Weist sich Angular global `window.angular` zu, wenn es als CommonJS-Modul geladen wird?.
  2. ProviderPlugin scheint nicht zu funktionieren: es macht jQuery nicht für den globalen Namespace verfügbar; Stattdessen fügt es für jedes Modul, das vom globalen Namen jQuery abhängt, ein require('jquery') drin. Ich bin mir nicht 100% sicher, aber es sieht so aus, als würde Angular nicht zugreifen jQuery direkt aus dem globalen Namensraum, stattdessen wird versucht, darauf zuzugreifen window.jQuery in bindJQuery Funktion, daher hilft dieser Ansatz nicht: Setzen Sie jQuery mit Webpack einem echten Window-Objekt aus.
  3. Aus dem gleichen Grund wie ProviderPlugin, imports-loader scheint untauglich: Angular will window.jQuerynicht nur jQuery.
  4. Mit expose-loadermacht es jquery zum Fensterobjekt. Mein Problem war, dass Babel alle seine Importe im resultierenden Code an die Spitze des Moduls hievt. Daher, obwohl require(expose?jquery!jquery) war vorher import angular from "angular" in Quelldateien, im Bündel require("angular") befindet sich am Anfang der Datei, vor jquery, sodass jquery zum Zeitpunkt des Imports von Angular noch nicht verfügbar ist. Ich frage mich, wie man Webpack-Loader mit ECMA6-Importsyntax verwendet.
  5. Es gab einen Anwendungsvorschlag import Syntax statt require Syntax mit jquery: import "jquery" oder import $ from "jquery"nicht require(jquery): (Petr Averyanov: How to use Webpack loaders syntax ( imports/exports/expose) with ECMAScript 6 imports?). jquery-Quellcode ist mit einer speziellen Hülle umwickeltdie angibt, wie jquery erforderlich ist (mit AMD/require, CommonJS oder global mit <script> Aussage). Basierend darauf setzt es ein spezielles Argument noGlobal für jquery fabric und entweder schafft window.jQuery oder nicht, basierend auf dem Wert von noGlobal. Ab jquery 2.2.4 auf import "jquery" noGlobal === true und window.jQuery wird nicht erstellt. IIRC, einige ältere Versionen von jquery wurden nicht erkannt import als CommonJS importieren und hinzufügen imported jquery in den globalen Namensraum, wodurch Winkel ihn verwenden konnte.

Details: hier ist meine app.js:

'use strict';

require("expose?$!expose?jQuery!jquery");
require("metisMenu/dist/metisMenu");
require("expose?_!lodash");
require("expose?angular!angular");

import angular from "angular";
import "angular-animate";
import "angular-messages";
import "angular-resource";
import "angular-sanitize";
import "angular-ui-router";
import "bootstrap/dist/css/bootstrap.css";
import "font-awesome/css/font-awesome.css";
import "angular-bootstrap";

require("../assets/styles/style.scss");
require("../assets/fonts/pe-icon-7-stroke/css/pe-icon-7-stroke.css");

// Import all html files to put them in $templateCache
// If you need to use lazy loading, you will probably need
// to remove these two lines and explicitly require htmls
const templates = require.context(__dirname, true, /\.html$/);
templates.keys().forEach(templates);

import HomeModule from "home/home.module";
import UniverseDirectives from "../components/directives";

angular.module("Universe", [
    "ngAnimate",
    "ngMessages",
    "ngResource",
    "ngSanitize",
    "ui.router",
    "ui.bootstrap",

    HomeModule.name,
    UniverseDirectives.name,
])
.config(function($urlRouterProvider, $locationProvider, $stateProvider){
    // $urlRouterProvider.otherwise("https://stackoverflow.com/");

    // $locationProvider.html5Mode(true);

    $stateProvider
      .state('test', {
        url: "/test",
        template: "This is a test"
      });
});

  • Wie laden Sie Angular und jQuery in Ihre Anwendung? Sind sie im Bündel oder werden sie vom CDN geladen?

    – Weiler Hakobyan

    17. März 2016 um 16:18 Uhr

  • @HamletHakobyan Sie sind beide im Webpack-Bundle.

    – Boris Burkow

    17. März 2016 um 16:19 Uhr

  • welche Winkelversion verwendest du?

    – Maimann

    17. März 2016 um 16:21 Uhr

  • @maioman Eckig 1.4.

    – Boris Burkow

    17. März 2016 um 16:23 Uhr

  • wenn du jquery vor angle lädst. angle ist schlau genug, um jquery über jqlite zu verwenden

    – Ji_in_coding

    17. März 2016 um 16:25 Uhr

Benutzer-Avatar
Nieten

Habe diese Antwort von John-Reilly bekommen:
Der mysteriöse Fall von Webpack Angle und JQuery

Die Antwort von bob-sponge ist nicht ganz richtig – das Provide-Plugin führt tatsächlich eine Textersetzung für Module durch, die es verarbeitet, also müssen wir bereitstellen window.jQuery (was eckig sucht) und nicht nur jQuery.

In deiner webpack.config.js Sie müssen Ihren Plugins den folgenden Eintrag hinzufügen:

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

Dies verwendet das Webpack Plugin bereitstellen und zum Zeitpunkt der Webpackifizierung (© 2016 John Reilly) werden alle Verweise im Code auf window.jQuery durch einen Verweis auf das Webpack-Modul ersetzt, das jQuery enthält. Wenn Sie sich also die gebündelte Datei ansehen, sehen Sie, dass der Code, der die window Objekt für jQuery ist das geworden:

jQuery = isUndefined(jqName) ?
  __webpack_provided_window_dot_jQuery : // use jQuery (if present)
    !jqName ? undefined : // use jqLite
    window[jqName]; // use jQuery specified by `ngJq`

Das stimmt; webpack versorgt Angular noch mit jQuery
nicht Platzierung a jQuery Variable auf die window. Ordentlich oder?

  • ICH LIEBE DICH, ich habe 2 Tage damit verbracht, die verrückten Fehler deswegen herauszufinden.

    – tinyCoder

    27. Oktober 2020 um 14:26 Uhr

Benutzer-Avatar
Lukas Chen

!!Aktualisieren

Anscheinend müssen Sie im ES6-Beispiel immer noch die gemeinsamen Js verwenden, die für eckig erforderlich sind.

import $ from "jquery"

window.$ = $;
window.jQuery = $;

var angular = require("angular");

unten ist die ursprüngliche Antwort



Ich möchte eine einfachere Lösung anstreben. Machen Sie jQuery einfach zu einem globalen Fenster, damit Angular es erkennen kann:

var $ = require("jquery")

window.$ = $;
window.jQuery = $;

var angular = require("angular");

oder in deinem Fall (VERLAUFT):

import $ from "jquery"

window.$ = $;
window.jQuery = $;

import angular from "angular";

Ich hoffe das hilft 🙂

  • Deine Lösung ist gut. Außerdem habe ich herausgefunden, dass du es einfach kannst import jquery; import angular und angle sollten jquery erkennen (siehe Update zu Frage Nr. 5). Könntest du mal testen, ob das geht?

    – Boris Burkow

    26. Juli 2016 um 12:19 Uhr


  • Hm, ich habe es selbst getestet. MIT jquery 2.2.4 noGlobal=true mit import $ from "jquery" und import "jquery".

    – Boris Burkow

    26. Juli 2016 um 14:38 Uhr

  • Und was ist Ihr Ergebnis?

    – Lukas Chen

    27. Juli 2016 um 5:26 Uhr

  • Ah, sorry, das Ergebnis ist negativ – es funktioniert nicht. jqueryDer Wrapper von erkennt, dass es als eine Variante von CommonJS importiert wurde, setzt noGlobal auf true und setzt window.$ nicht, daher erkennt Angular es nicht.

    – Boris Burkow

    27. Juli 2016 um 9:50 Uhr

  • Können Sie meine Lösung mit noGlobal=false verwenden.

    – Lukas Chen

    27. Juli 2016 um 10:00 Uhr

In Ihrem Fall ist besser zu verwenden Plugin bereitstellen. Fügen Sie einfach diese Zeilen zu Ihrer Webpack-Konfiguration im Plugin-Bereich hinzu und jquery wird in Ihrer App verfügbar sein:

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

  • Hallo, Bob Schwamm! Das hat nicht geholfen: angular.element -> function JQLite(). Allerdings verstehe ich nicht, warum angle überhaupt in den globalen Namensraum exportiert wurde – ich habe die Zeile auskommentiert require("expose?angular!angular");.

    – Boris Burkow

    18. März 2016 um 8:57 Uhr

  • Tatsächlich habe ich importiert angular zweimal im app.js – lokal und global – was Verwirrung stiften könnte. Ich möchte den lokalen Import entfernen, aber ich kann nicht. OHNE das Lokal import angular from "angular"; Browser flucht webpack:///./bower_components/angular-animate/angular-animate.js?:9 Uncaught TypeError: Cannot read property 'noop' of undefined. Der Globus require("expose?angular!angular"); wurde zu Debugging-Zwecken erstellt und wird in der Produktion entfernt.

    – Boris Burkow

    18. März 2016 um 9:04 Uhr

  • Stellen Sie sicher, dass Sie jQuery 2.1+ und verwenden window.jQuery ist nicht undefined

    – Bob Schwamm

    18. März 2016 um 10:24 Uhr

  • Endlich gelöst. Es war Babel zu verdanken, das Wichtigkeit vor Erfordernis stellte. Es hat meinen Code neu angeordnet, sodass Winkel immer importiert wurde, bevor jquery erforderlich war. Verdammt. 🙂

    – Boris Burkow

    21. März 2016 um 20:41 Uhr

  • @invincibleDudess Ich habe es nicht ganz gelöst – ich habe gerade alle ECMA6 ersetzt imports mit CommonJS requires, wie es in Webpack nicht gesagt werden kann require("expose?$!expose?jQuery!jquery"); in ECMA6 import Syntax. Obwohl es eine andere Lösung gibt. Es stellte sich heraus, dass jquery, lodash und angularWenn imported in ECMA6 fügen Sie sich einfach selbst hinzu window Objekt und werden global verfügbar, also brauchst du sie nicht expose Lader. Sie können also einfach ECMA6 verwenden imports überall, es sei denn, einige Ihrer kleineren Bibliotheken müssen verfügbar gemacht werden. Nur für den Fall, dass ich dabei blieb require bis jetzt.

    – Boris Burkow

    15. April 2016 um 8:55 Uhr


Benutzer-Avatar
GabLeRoux

Es gibt diesen japanischen Artikel Ich möchte jQuery und nicht jQLite in Webpack + AngularJS verwenden das scheint über dasselbe Problem zu sprechen (ich kann übrigens noch kein Japanisch). Ich habe Google verwendet, um ins Englische zu übersetzen, Credits gehen an Zither für diese nette Antwort.

Er bietet vier Möglichkeiten, dies zu lösen:

  1. Direkt dem Fenster zuweisen (nicht wirklich cool)

    window.jQuery = require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  2. Verwenden Sie die Expose-Loader (ok, aber nicht so cool)

    npm install --saveDev expose-loader
    

    webpack.config.js

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    Verwendungszweck:

    require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  3. Verwenden Expose-Loader (besser)

    npm install --saveDev expose-loader
    

    webpack.config.js

        module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        module: {
            loaders: [{
                test: /\/angular\.js$/,
                loader: "imports?jQuery=jquery"
            }, {
                test: /\/jquery.js$/,
                loader: "expose?jQuery"
            }]
        }
    };
    

    Verwendungszweck:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  4. Verwenden Plugin bereitstellen (Beste Lösung)

    Dies ist eigentlich dasselbe wie die akzeptierte Antwort von Studds hier

    module.exports = {
        entry: "./index",
        output: {
            path: __dirname,
            filename: "bundle.js"
        },
        plugins: [
            new webpack.ProvidePlugin({
                "window.jQuery": "jquery"
            })
        ],
    };
    

    Verwendungszweck:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    

Ich dachte, ich würde das hier teilen, da wir genau das gleiche Problem hatten. Wir haben die benutzt expose-loader Lösung in unserem Projekt mit Erfolg. Ich nehme an, dass die ProvidePlugin die jquery direkt injiziert window ist auch eine gute idee.

Benutzer-Avatar
Philho

Also gebe ich meine Lösung, die eigentlich ein Mashup der Antwort von @BobSponge und der Hinweise / Kommentare von @Bob ist. Also nichts Originelles, nur zeigen, was für mich funktioniert (in einem Projekt, das Babel / ES6, BTW nicht verwendet) und versuchen zu erklären, warum es funktioniert …

Der (letzte) Trick besteht in der Tat darin, die zu verwenden Lader aussetzen.

Wie auf ihrer Seite erklärt, müssen wir eintragen module.loaders:

{ test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" },

Außerdem im plugins Liste, ich habe:

        new webpack.ProvidePlugin(
        {
            $: 'jquery',
            jQuery: 'jquery',
            _: 'lodash',
            // [...] some other libraries that put themselves in the global scope.
            //angular: 'angular', // No, I prefer to require it everywhere explicitly.
        }),

die tatsächlich die globalen Vorkommen dieser Variablen im Code findet und sie in Variablen erfordert, die für jedes Modul lokal sind. Es erleichtert die Migration eines bestehenden Projekts (von RequireJS zu Webpack), so wie ich es tue … Ich denke, wir können auf dieses Plugin verzichten, wenn Sie es vorziehen, explizit in Ihren Importen zu sein.

Und was wichtig ist (glaube ich), benötige ich am Einstiegspunkt der Anwendung in der Reihenfolge, in der ich sie haben möchte. In meinem Fall habe ich ein Anbieter-Bundle erstellt, das ist also die Bestellung in diesem Bundle.

require('jquery');

require('lodash');
// [...]

var angular = require('angular');
// Use the angular variable to declare the app module, etc.

Webpack fügt die Bibliotheken in der angezeigten Reihenfolge zum entsprechenden Bundle hinzu requires (es sei denn, Sie verwenden ein Plugin / einen Loader, der sie neu anordnet). Aber die Importe sind isoliert (kein globales Leck), sodass Angular die jQuery-Bibliothek nicht sehen konnte. Daher die Notwendigkeit expose. (Ich habe es versucht window.jQuery = require('jquery'); stattdessen, aber es hat nicht funktioniert, vielleicht ist es zu spät.)

  • Was meinst du mit in the vendor bundle, as I made one ? Ich meine… wo genau schreibst du diesen Code, der die Reihenfolge definiert?

    – refaelos

    16. November 2016 um 20:00 Uhr

  • Ich musste mich ein paar Mal neu lesen, um zu verstehen, was ich meinte… 🙂 Ich möchte, dass diese Bibliotheken im Vendor-Bundle bestellt werden. Das requires in der richtigen Reihenfolge werden in das App-Bundle gelegt, dh. natürlich im App-Code.

    – Philho

    17. November 2016 um 13:54 Uhr

  • Was meinst du mit in the vendor bundle, as I made one ? Ich meine… wo genau schreibst du diesen Code, der die Reihenfolge definiert?

    – refaelos

    16. November 2016 um 20:00 Uhr

  • Ich musste mich ein paar Mal neu lesen, um zu verstehen, was ich meinte… 🙂 Ich möchte, dass diese Bibliotheken im Vendor-Bundle bestellt werden. Das requires in der richtigen Reihenfolge werden in das App-Bundle gelegt, dh. natürlich im App-Code.

    – Philho

    17. November 2016 um 13:54 Uhr

1256670cookie-checkWebpack: Wie kann jQuery automatisch erkannt und als angle.element anstelle von jqLite verwendet werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy