Webpack:如何使角度自动检测 jQuery 并将其用作 angular.element 而不是 jqLite?
Posted
技术标签:
【中文标题】Webpack:如何使角度自动检测 jQuery 并将其用作 angular.element 而不是 jqLite?【英文标题】:Webpack: how to make angular auto-detect jQuery and use it as angular.element instead of jqLite? 【发布时间】:2016-07-04 02:50:56 【问题描述】:我正在使用 Webpack 构建一个 Angular 1.4 项目。该项目使用了几个 jQuery 插件,这些插件被包装在 angular 指令中。这些指令在内部使用angular.element
,可能暗示 angular.element 是真正的 jQuery,而不是 jqLite。
我希望 Angular 自动检测 jQuery 并使用它来代替 jqLite。我尝试在我的入口点模块 app.js
:require('jquery')
中本地要求 jquery,并使用 require(expose?$!expose?jQuery!jquery)
全局公开 jQuery。
不过,不管我做什么,angular.element
指的是 jqLite。
我的研究得出了几个发现:
-
即使作为 CommonJS 模块导入时,Angular 也会将自己分配给全局变量
window.angular
,因此我不需要使用 Webpack Does Angular assign itself to `window.angular` globally, when loaded as CommonJS module? expose
。
ProviderPlugin 似乎不能解决问题:它不会将 jQuery 暴露给全局命名空间;相反,对于依赖于全局名称 jQuery 的每个模块,它会在其中插入 require('jquery')
。我不是 100% 确定,但看起来 Angular 没有直接从全局命名空间访问 jQuery
,而是尝试在 bindJQuery
函数中访问 window.jQuery
,所以这种方法没有帮助:Expose jQuery to real Window object with Webpack .
出于与 ProviderPlugin 相同的原因,imports-loader
似乎不合适:Angular 想要 window.jQuery
,而不仅仅是 jQuery
。
使用expose-loader
,jquery 使其成为窗口对象。 我的问题是 Babel 在生成的代码中将其所有导入提升到模块的顶部。因此,虽然require(expose?jquery!jquery)
在源文件中位于import angular from "angular"
之前,但在bundle 中require("angular")
位于文件顶部,在jquery 之前,因此在导入Angular 时,jquery 尚不可用。我想知道,如何使用带有 ECMA6 导入语法的 Webpack 加载器。
有人建议在 jquery 中使用 import
语法而不是 require
语法:import "jquery"
或 import $ from "jquery"
,而不是 require(jquery)
:(Petr Averyanov:How to use Webpack loaders syntax ( imports/exports/expose) with ECMAScript 6 imports?)。 jquery 源代码是wrapped with a special wrapper,它标识了 jquery 是如何被需要的(使用 AMD/require、CommonJS 或全局使用 <script>
语句)。基于此,它为 jquery fabric 设置了一个特殊的参数noGlobal
,并根据noGlobal
的值创建或不创建window.jQuery
。从 jquery 2.2.4 开始,不会创建 import "jquery"
noGlobal === true
和 window.jQuery
。 IIRC,一些旧版本的 jquery 没有将 import
识别为 CommonJS 导入并将 import
ed jquery 添加到全局命名空间,这允许 angular 使用它。
详情:这是我的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"
);
);
【问题讨论】:
如何将 Angular 和 jQuery 加载到应用程序中?它们是捆绑在一起的还是从 CDN 加载的? @HamletHakobyan 它们都在 webpack 包中。 你用的是什么角度版本? @maioman Angular 1.4. 如果你在 Angular 之前加载 jquery。 angular 足够聪明,可以在 jqlite 上使用 jquery 【参考方案1】:从john-reilly得到这个答案:The mysterious case of webpack angular and jquery
bob-sponge 的回答不太正确 - 提供插件实际上是在它处理的模块上进行文本替换,所以我们需要提供 window.jQuery
(这是 angular 正在寻找的)而不仅仅是 @987654326 @。
在您的
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 天时间试图找出疯狂的错误。【参考方案2】:!!更新
显然你仍然需要在 ES6 示例中使用 commonJs 对 angular 的要求。
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";
我希望这会有所帮助:)
【讨论】:
您的解决方案很好。此外,我发现您可以只使用import jquery; import angular
并且 angular 应该检测到 jquery(请参阅问题的更新,#5)。你能测试一下,如果可行吗?
嗯,我自己测试过。使用 jquery 2.2.4 noGlobal=true 和 import $ from "jquery"
和 import "jquery"
。
你的结果是什么?
啊,对不起,结果是否定的——它不起作用。 jquery
的包装器识别出它是作为 CommonJS 的风格导入的,将 noGlobal 设置为 true 并且不设置 window.$,因此 angular 不会检测到它。
你能使用我的解决方案吗? noGlobal=false。【参考方案3】:
在您的情况下,最好使用ProvidePlugin。只需将此行添加到插件部分的 webpack 配置中,jquery 就会在您的应用程序中可用:
new webpack.ProvidePlugin(
"$": "jquery",
"jQuery": "jquery"
)
【讨论】:
嗨,鲍勃·海绵!这没有帮助:angular.element
-> function JQLite()
。虽然,我不明白,为什么 angular 被导出到全局命名空间 - 我已经注释掉了 require("expose?angular!angular");
行。
事实上,我在 app.js
中导入了两次 angular
- 本地和全局 - 这可能会引起混淆。我想删除本地导入,但我不能。没有本地的import angular from "angular";
浏览器会诅咒webpack:///./bower_components/angular-animate/angular-animate.js?:9 Uncaught TypeError: Cannot read property 'noop' of undefined
。全局 require("expose?angular!angular");
用于调试目的,将在生产中删除。
确保您使用的是 jQuery 2.1+ 并且window.jQuery
不是undefined
终于解决了。这是由于 Babel 将导入置于需求之前。它重新排列了我的代码,以便在需要 jquery 之前始终导入 angular。该死。 :)
@invincibleDudess 我没有完全解决它 - 我只是用 CommonJS require
s 替换了所有的 ECMA6 import
s,因为 Webpack 中没有办法在 ECMA6 @987654336 中说 require("expose?$!expose?jQuery!jquery");
@ 句法。虽然,还有另一种解决方案。原来jquery
、lodash
和angular
,当import
ed 在ECMA6 中只是将自己添加到window
对象并成为全局可用的,所以对于他们你不需要expose
加载器。所以你可以在任何地方使用 ECMA6 import
s,除非你的一些小库需要暴露。以防我到目前为止坚持使用require
。【参考方案4】:
有这篇日文文章I want to use the jQuery not jQLite in webpack + AngularJS 似乎在谈论同样的问题(顺便说一句,我还不懂日文)。我用谷歌翻译成英文,感谢 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(最佳解决方案)
这里其实和studds's accepted answer是一样的
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
解决方案。我想直接在window
中注入jquery的ProvidePlugin
也是个好主意。
【讨论】:
【参考方案5】:所以,我给出了我的解决方案,它实际上是@BobSponge 答案和@Bob 的提示/cmets 的混搭。所以没有什么原创的,只是展示了对我有用的东西(在一个不使用 Babel / ES6,顺便说一句的项目中)并试图解释它为什么有效......
(最终)技巧确实是使用expose loader。
正如他们页面中所解释的,我们必须输入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
s 的顺序将库添加到相关包中(除非您使用重新排序它们的插件/加载器)。
但是导入是隔离的(没有全局泄漏),因此 Angular 无法看到 jQuery 库。因此需要expose
。 (我尝试了window.jQuery = require('jquery');
,但没有成功,也许为时已晚。)
【讨论】:
in the vendor bundle, as I made one
是什么意思?我的意思是......你把定义顺序的代码放在哪里?
我不得不重新阅读自己几次才能理解我的意思... :-) 我希望在供应商捆绑包中订购这些库。 require
s 以正确的顺序放入应用程序包中,即。当然是在应用代码中。以上是关于Webpack:如何使角度自动检测 jQuery 并将其用作 angular.element 而不是 jqLite?的主要内容,如果未能解决你的问题,请参考以下文章
如何使 webpack typescript 响应 webpack-dev-server 配置以自动构建和重新加载页面
如何使用 CdkScrollable 检测角度材料 2 自动完成列表上的滚动事件