Подтвердить что ты не робот

Webpack: как сделать angular автоматическое определение jQuery и использовать его как angular.element вместо jqLite?

Я использую Webpack для создания проекта Angular 1.4. В проекте используется несколько плагинов jQuery, которые завернуты в директивы Angular. Эти директивы внутренне используют angular.element, вероятно, подразумевая, что angular.элемент - это реальный jQuery, а не jqLite.

Я хочу, чтобы Angular автоматически обнаруживал jQuery и использовал его вместо jqLite. Я попытался локально найти jquery в моем модуле точки входа app.js: require('jquery') и разоблачить jQuery глобально с помощью require(expose?$!expose?jQuery!jquery).

Тем не менее, что бы я ни делал, angular.element относится к jqLite.


Мои исследования привели к нескольким выводам:

  • Даже если импортировано как модуль CommonJS, Angular присваивает себя глобальной переменной window.angular, поэтому мне не нужно expose его с помощью Webpack: Does Angular присваивается `window.angular` глобально при загрузке в качестве модуля CommonJS?.
  • ProviderPlugin, похоже, не выполняет трюк: он не предоставляет jQuery для глобального пространства имен; вместо этого для каждого модуля, который зависит от глобального имени jQuery, он вставляет require('jquery') в него. Я не уверен на 100%, но выглядит как Angular не получает доступ к jQuery из глобального пространства имен напрямую, вместо этого он пытается получить доступ к window.jQuery в bindJQuery, поэтому этот подход не помогает: Открыть jQuery для реального объекта Window с помощью Webpack.
  • По той же причине, что и ProviderPlugin, imports-loader кажется непригодным: Angular хочет window.jQuery, а не только jQuery.
  • С expose-loader jquery делает это объекту окна.. Моя проблема заключалась в том, что Babel поднимает весь свой импорт до вершины модуля в полученном коде. Следовательно, хотя require(expose?jquery!jquery) был до import angular from "angular" в исходных файлах, в пакете require("angular") находится вверху файла, перед jquery, поэтому к моменту импорта Angular jquery пока недоступен. Интересно, как использовать загрузчики Webpack с синтаксисом импорта ECMA6.
  • Было предложено использовать синтаксис import вместо синтаксиса require с jquery: import "jquery" или import $ from "jquery", а не require(jquery): (Петр Аверьянов: Как использовать синтаксис загрузчиков Webpack (импорт/экспорт/публикация) с помощью импорта ECMAScript 6?). Исходный код jquery обернут специальной оболочкой, которая idenitizes как требуется jquery (с AMD/require, CommonJS или глобально с оператором <script>), Исходя из этого, он устанавливает специальный аргумент noGlobal для jquery fabric и либо создает window.jQuery, либо нет, основываясь на значении noGlobal. Начиная с jQuery 2.2.4, после import "jquery" noGlobal === true и window.jQuery не создается. IIRC, некоторые более старые версии jquery не распознали import как импорт CommonJS и добавили import ed jquery в глобальное пространство имен, что позволило использовать Angular.

Подробности: здесь my 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('/');

    // $locationProvider.html5Mode(true);

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

Ответ 1

Получил этот ответ от john-reilly:
Таинственный случай webpack angular и jquery

bob-sponge ответ не совсем прав - плагин Provide фактически выполняет замену текста на модули, которые он обрабатывает, поэтому нам нужно предоставить window.jQuery (который это то, что ищет angular), а не только jQuery.

В вашем webpack.config.js вам нужно добавить следующая запись в ваши плагины:

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

Это использует webpack ProvidePlugin и, в момент webpackification (© 2016 John Reilly) все ссылки в коде на window.jQuery будет заменен ссылкой на модуль webpack который содержит jQuery. Поэтому, когда вы смотрите на связанный файл, вы увидите что код, который проверяет объект window для jQuery, стал это:

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

Это право; webpack предоставляет angular jQuery, пока не, помещая переменную jQuery в window. Аккуратный ха?

Ответ 2

!! Update

По-видимому, вам все равно нужно использовать требование commonJs для angular в примере ES6.

import $ from "jquery"

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

var angular = require("angular");

ниже - исходный ответ



Я хочу использовать более простое решение. Просто сделайте jQuery глобальным окном, чтобы angular мог его распознать:

var $ = require("jquery")

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

var angular = require("angular");

или в вашем случае (OUT DATED):

import $ from "jquery"

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

import angular from "angular";

Надеюсь, это поможет:)

Ответ 3

В вашем случае лучше использовать ProvidePlugin. Просто добавьте эти строки в свою конфигурацию webpack в разделе плагинов, и jQuery будет доступен в вашем приложении:

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

Ответ 4

Итак, я даю свое решение, которое на самом деле представляет собой mashup ответа @BobSponge и подсказки/комментарии @Bob. Так что ничего оригинального, просто показывая, что работает для меня (в проекте, не использующем Babel/ES6, BTW) и пытаюсь объяснить, почему он работает...

(окончательный) трюк действительно должен использовать выставить загрузчик.

Как объяснено на их странице, мы должны ввести module.loaders:

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

Кроме того, в списке plugins у меня есть:

        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.
        }),

который фактически находит глобальные вхождения этих переменных в код и требует их в переменных, локальных для каждого модуля. Это облегчает миграцию существующего проекта (от RequireJS до Webpack), как и я... Я думаю, что мы можем обойтись без этого плагина, если вы предпочитаете быть явным в своих импортах.

И, что важно (я думаю), в точке входа приложения, я требую их в том порядке, в котором я им нужен. В моем случае я сделал пакет поставщиков, так что порядок в этом комплекте.

require('jquery');

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

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

Webpack добавит библиотеки в соответствующий пакет в том порядке, в котором он видит require (если вы не используете плагин/загрузчик, которые их переупорядочивают). Но импорт изолирован (нет глобальной утечки), поэтому Angular не смог увидеть библиотеку jQuery. Отсюда необходимость expose. (Я попробовал window.jQuery = require('jquery');, но это не сработало, возможно, уже слишком поздно.)

Ответ 5

Есть эта японская статья Я хочу использовать jQuery, а не jQLite в webpack + AngularJS, который, похоже, говорит об одной и той же проблеме (I не знаю, японцы, но кстати). Я использовал Google для перевода на английский язык, кредиты идут на cither для этого приятного ответа.

Он предоставляет четыре способа решить эту проблему:

  • Назначьте непосредственно окну (не очень круто)

    window.jQuery = require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  • Используйте expose-loader (хорошо, но не так круто)

    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"
            }]
        }
    };
    

    использование:

    require('jquery');
    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  • Используйте expose-loader (лучше)

    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"
            }]
        }
    };
    

    использование:

    var angular = require('angular');
    console.log(angular.element('body'));
    //[body, prevObject: jQuery.fn.init[1], context: document, selector: "body"]
    
  • Используйте ProvidePlugin (лучшее решение)

    Это на самом деле то же самое, что ответ твёрды принимал здесь

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

    использование:

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

Я думал, что поделюсь этим, так как у нас была такая же проблема. Мы успешно использовали решение expose-loader в нашем проекте. Я полагаю, что ProvidePlugin, который вводит jquery непосредственно в window, также является хорошей идеей.