我正在使用browserify-shim,我想使用通用的jQuery插件。我多次查看了Browserify-shim文档,我似乎无法理解发生了什么和/或它如何知道放置插件的位置,附加到jQuery对象等。这是我的package.json文件的样子:
"browser": {
"jquery": "./src/js/vendor/jquery.js",
"caret": "./src/js/vendor/jquery.caret.js"
},
"browserify-shim": {
"caret": {
"depends": ["jquery:$"]
}
}
根据browserify-shim文档中给出的示例,我不想指定导出,因为这个插件(以及大多数(如果不是全部)jQuery插件)将自己附加到jQuery对象。除非我上面做错了,否则当我使用它时,我不明白为什么它不起作用(我得到一个错误告诉我函数未定义)。见下文:
$('#contenteditable').caret(5); // Uncaught TypeError: undefined is not a function
所以我的问题是,如何使用browserify和browserify-shim配置一个通用的jQuery插件(它将自己附加到jQuery对象上)?
答案 0 :(得分:97)
在重新审视并尝试了更多的东西之后,我终于围绕着浏览器shim正在做什么以及如何使用它。对我来说,在我最终了解如何使用browserify-shim之前,我必须掌握一个关键原则。对于两种不同的用例,有两种方法可以使用browserify-shim:exposing&垫补。
我们假设您只想在标记中插入脚本标记(出于测试或性能原因,如缓存,CDN等)。通过在标记中包含脚本标记,浏览器将点击脚本,运行它,并且很可能在窗口对象上附加属性(在JS中也称为全局)。当然,这可以通过myGlobal
或window.myGlobal
来访问。但是这两种语法都存在问题。它没有遵循CommonJS规范,这意味着如果一个模块开始支持CommonJS语法(require()
),你就无法利用它。
Browserify-shim允许您指定一个您喜欢"暴露"通过CommonJS require()
语法。请记住,您可以执行var whatever = global;
或var whatever = window.global;
,但您不能var whatever = require('global')
并希望它能为您提供正确的lib /模块。不要对变量的名称感到困惑。它可能是任意的。您实际上将全局变量设为局部变量。这听起来很愚蠢,但它是浏览器中JS的悲惨状态。同样,希望是一旦lib支持CommonJS语法,它将永远不会通过窗口对象上的全局附加自身。这意味着你必须使用require()
语法并将其分配给局部变量,然后在需要的地方使用它。
注意:我发现变量命名在browserify-shim docs / examples中略显混乱。请记住,关键是你要包含一个lib ,好像它是一个正常运行的CommonJS模块。所以你最终要做的就是告诉browserify当你需要myGlobal require('myGlobal')
时,你实际上只想在窗口对象window.myGlobal
上获得全局属性。
事实上,如果您对需求功能实际上做了什么感到好奇,那就非常简单了。以下是发生在幕后的事情:
var whatever = require('mygGlobal');
...变为
var whatever = window.mygGlobal;
所以在这个背景下,让我们看看我们如何在browserify-shim配置中公开一个模块/ lib。基本上,你告诉browserify-shim两件事。当您调用require()
时可以访问的名称以及它应该在窗口对象上找到的全局名称。这就是global:*
语法的来源。让我们来看一个例子。我想将jquery作为index.html中的脚本标记放入,以便我获得更好的性能。这是我在配置中需要做的事情(这将在package.json或外部配置JS文件中):
"browserify-shim": {
"jquery": "global:$"
}
所以这就是这意味着什么。我在其他地方包含了jQuery(记住,browserify-shim不知道我们在哪里放置我们的标签,但它不需要知道),但我想要的只是给$
当我需要带有字符串参数" jquery"的模块时,窗口对象上的属性。进一步说明。我也可以这样做:
"browserify-shim": {
"thingy": "global:$"
}
在这种情况下,我必须通过" thingy"作为require函数的参数,以便获取jQuery对象的实例(它只是从window.$
获取jQuery):
var $ = require('thingy');
是的,同样,变量名可以是任何东西。 $
与实际jQuery库使用的全局属性$
相同没有什么特别之处。尽管使用相同的名称以避免混淆是有意义的。这最终引用窗口对象上的$
属性,由package.json中global:$
对象中的browserify-shim
值选择。
好的,这几乎涵盖了暴露。 browserify-shim的另一个主要功能是填充。那是什么? Shimming与暴露基本相同,除了在HTML标记中包含lib或模块与脚本标记之类的东西,你告诉browserify-shim在哪里抓取JS文件。无需使用global:*
语法。所以让我们回顾一下我们的jQuery示例,但这次假设我们不是从CDN加载jQuery,而只是将它与所有JS文件捆绑在一起。所以这是配置的样子:
"browser": {
"jquery": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
"jquery": "$"
},
这个配置告诉browserify-shim从指定的本地路径加载jQuery,然后从window对象中获取$属性,并在需要带有字符串参数的jQuery时返回到" jquery&#34的require函数;。同样,为了便于说明,您还可以将其重命名为其他任何内容。
"browser": {
"thingy": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
"thingy": "$"
},
可能需要:
var whatever = require('thingy');
我建议您查看browserify-shim文档,了解有关使用exports
属性的长手语法的更多信息,以及depends
属性,该属性允许您告诉browserify-shim如果lib依赖于另一个lib /模块。我在这里解释的内容适用于两者。我希望这有助于其他人努力了解browserify-shim实际上在做什么以及如何使用它。
匿名填充是browserify-shim的替代方法,它允许您使用browserify的--standalone
选项将jQuery等库转换为UMD模块。
$ browserify ./src/js/vendor/jquery.js -s thingy > ../dist/jquery-UMD.js
如果将其删除到脚本标记中,则此模块会将jQuery添加到窗口对象thingy
。当然它也可以是$
或任何你喜欢的。
但是,如果它require
加入了您的浏览器应用套件var $ = require("./dist/jquery-UMD.js");
,您将在应用内部使用jQuery,而无需将其添加到窗口对象中。< / p>
此方法不需要browserify-shim并利用jQuery的CommonJS感知功能,它会查找module
对象并将noGlobal
标志传递到其工厂,而不会告诉它将自己附加到窗口对象。
答案 1 :(得分:12)
对于每个人,谁在寻找一个具体的例子:
以下是jQuery插件的package.json
和app.js
文件的示例,该插件将自身附加到jQuery
/ $
对象,例如:$('div').expose()
。当我需要时,我不希望jQuery
成为全局变量(window.jQuery
),这就是jQuery设置为'exports': null
的原因。但是,因为插件期望它可以附加到它的全局jQuery
对象,所以必须在文件名后的依赖项中指定它:./jquery-2.1.3.js:jQuery
。此外,您需要在使用插件时实际导出jQuery
全局,即使您不想这样做,因为插件不会起作用(至少是这个特定的插件)。
<强>的package.json 强>
{
"name": "test",
"version": "0.1.0",
"description": "test",
"browserify-shim": {
"./jquery-2.1.3.js": { "exports": null },
"./jquery.expose.js": { "exports": "jQuery", "depends": [ "./jquery-2.1.3.js:jQuery" ] }
},
"browserify": {
"transform": [
"browserify-shim"
]
}
}
<强> app.js 强>
// copy and delete any previously defined jQuery objects
if (window.jQuery) {
window.original_jQuery = window.jQuery;
delete window.jQuery;
if (typeof window.$.fn.jquery === 'string') {
window.original_$ = window.$;
delete window.$;
}
}
// exposes the jQuery global
require('./jquery.expose.js');
// copy it to another variable of my choosing and delete the global one
var my_jQuery = jQuery;
delete window.jQuery;
// re-setting the original jQuery object (if any)
if (window.original_jQuery) { window.jQuery = window.original_jQuery; delete window.original_jQuery; }
if (window.original_$) { window.$ = window.original_$; delete window.original_$; }
my_jQuery(document).ready(function() {
my_jQuery('button').click(function(){
my_jQuery(this).expose();
});
});
在上面的例子中,我不希望我的代码设置任何全局变量,但我暂时不得不这样做,以使插件工作。如果您只需要jQuery,那么您可以执行此操作并且不需要任何解决方法:var my_jQuery = require('./jquery-2.1.3.js')
。如果您将jQuery公开为全局,那么您可以修改上面的package.json
示例,如下所示:
"browserify-shim": {
"./jquery-2.1.3.js": { "exports": "$" },
"./jquery.expose.js": { "exports": null, "depends": [ "./jquery-2.1.3.js" ] }
希望能帮助一些正在寻找具体例子的人(就像我在找到这个问题时那样)。
答案 2 :(得分:1)
为了完整起见,这里有一个利用jQuery的CommonJS意识的方法,以避免担心污染window
对象而不需要实际需要填充。
window
对象在 ./ package.json 中,添加browser
节点以为资源位置创建别名。 这纯粹是为了方便,不需要实际填充任何东西,因为模块和全局空间之间没有通信(脚本标签)。
{
"main": "app.cb.js",
"scripts": {
"build": "browserify ./app.cb.js > ./app.cb.bundle.js"
},
"browser": {
"jquery": "./node_modules/jquery/dist/jquery.js",
"expose": "./js/jquery.expose.js",
"app": "./app.cb.js"
},
"author": "cool.blue",
"license": "MIT",
"dependencies": {
"jquery": "^3.1.0"
},
"devDependencies": {
"browserify": "^13.0.1",
"browserify-shim": "^3.8.12"
}
}
module
对象的存在并返回一个实例,而不会将其添加到window
对象。require
jquery并将其添加到module.exports
对象(以及需要共享的任何其他上下文)。$
并在插件中使用jQuery。app.cb.js
var $ = module.exports.jQuery = require("jquery");
require('expose');
$(document).ready(function() {
$('body').append(
$('<button name="button" >Click me</button>')
.css({"position": "relative",
"top": "100px", "left": "100px"})
.click(function() {
$(this).expose();
})
);
});
位于插件顶部
var jQuery = require("app").jQuery;
HTML中的
<script type="text/javascript" src="app.cb.bundle.js"></script>
jQuery使用的模式是,如果感知到CommonJS环境,则使用noGlobal
标志调用它的工厂。它不会向window
对象添加实例,并将一如既往地返回实例。
CommonJS上下文默认由 browserify 创建。下面是显示jQuery模块结构的bundle的简化摘录。为清楚起见,我删除了处理window
对象的同构处理的代码。
3: [function(require, module, exports) {
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = factory( global, true );
} else {
factory( global );
}
// Pass this if window is not defined yet
} )( window, function( window, noGlobal ) {
// ...
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
}) );
}, {}]
我找到的最好的方法是让节点模块系统工作,然后每次浏览后都能正常工作。
只需使用 jsdom 来填充window
对象,以便代码是同构的。然后,专注于让它在节点中工作。然后,在模块和全局空间之间填充任何流量,最后对其进行浏览,它将在浏览器中运行。
答案 3 :(得分:0)
我在使用wordpress。因此,我有点被迫使用window
对象中提供的wordpress core的jQuery。
当我尝试从npm使用slick()
插件时,它生成了slick()
未定义的错误。添加browserify-shim
并没有多大帮助。
我做了一些挖掘,发现require('jquery')
总是不一致。
在我的主题javascript文件中,它调用了wordpress core的jquery。
但是,在slick
jquery插件中,它从节点模块调用了最新的jquery。
最后,我能够解决它。因此,共享package.json
和gulpfile
配置。
<强>的package.json:强>
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:jQuery"
},
<强> gulpfile.babel.js:强>
browserify({entries: 'main.js', extensions: ['js'], debug: true})
.transform(babelify.configure({
presets: ["es2015"]
}))
.transform('browserify-shim', {global: true})
进行变换'browserify-shim'是至关重要的部分,我之前没想到。没有它browserify-shim
不一致。