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"
});
});
Ответы
Ответ 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
, также является хорошей идеей.