javascript 使用 Browserify-shim 配置通用 jQuery 插件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24835954/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 03:26:00  来源:igfitidea点击:

Configure a generic jQuery plugin with Browserify-shim?

javascriptconfigurationbrowserify

提问by Glen Selle

I'm using browserify-shim and I want to use a generic jQuery plugin. I have looked over the Browserify-shim docs multiple times and I just can't seem to understand what's going on and/or how it knows where to put plugins, attach to the jQuery object etc. Here's what my package.json file looks like:

我正在使用 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:$"]
  }
}

According the the example given on the browserify-shim documentation, I don't want to specify an exports because this plugin (and most if not all jQuery plugins) attach themselves to the jQuery object. Unless I'm doing something wrong above, I don't understand why it doesn't work (I get an error telling me the function is undefined) when I use it. See below:

根据 browserify-shim 文档中给出的示例,我不想指定导出,因为此插件(以及大多数(如果不是全部)jQuery 插件)将自身附加到 jQuery 对象。除非我在上面做错了什么,否则当我使用它时,我不明白为什么它不起作用(我收到一个错误,告诉我函数未定义)。见下文:

$('#contenteditable').caret(5);  // Uncaught TypeError: undefined is not a function

So my question is, how does one configure a generic jQuery plugin (which attaches itself to the jQuery object) with browserify and browserify-shim?

所以我的问题是,如何使用 browserify 和 browserify-shim 配置通用 jQuery 插件(将自身附加到 jQuery 对象)?

回答by Glen Selle

After revisiting this and trying some more things, I finally wrapped my head around what browserify-shim is doing and how to use it. For me, there was one key principle I had to grasp before I finally understood how to use browserify-shim. There are basically two ways to use browserify-shim for two different use cases: exposing & shimming.

在重新审视这个并尝试了更多的东西之后,我终于把我的头脑围绕在 browserify-shim 正在做什么以及如何使用它上。对我来说,在我最终理解如何使用 browserify-shim 之前,我必须掌握一个关键原则。基本上有两种方法可以将 browserify-shim 用于两个不同的用例:公开和填充。

Background

背景

Let's say you want to just drop in a script tag in your markup (for testing or performance reasons like caching, CDN & the like). By including a script tag in the markup the browser will hit the script, run it, and most likely attach a property on the window object (also known as a global in JS). Of course this can be accessed by either doing myGlobalor window.myGlobal. But there's an issue with either syntax. It doesn't follow the CommonJS spec which means that if a module begins supporting CommonJS syntax (require()), you're not able to take advantage of it.

假设您只想在标记中放入一个脚本标签(出于测试或性能原因,如缓存、CDN 等)。通过在标记中包含一个脚本标签,浏览器将点击脚本,运行它,并且很可能在 window 对象上附加一个属性(在 JS 中也称为全局)。当然,这可以通过执行myGlobal或来访问window.myGlobal。但是这两种语法都有问题。它不遵循 CommonJS 规范,这意味着如果模块开始支持 CommonJS 语法 ( require()),您将无法利用它。

The Solution

解决方案

Browserify-shim allows you to specify a global you'd like "exposed" through CommonJS require()syntax. Remember, you could do var whatever = global;or var whatever = window.global;but you could NOT do var whatever = require('global')and expect it to give you the right lib/module. Don't be confused about the name of the variable. It could be anything arbitrary. You're essentially making a global variable a local variable. It sounds stupid, but its the sad state of JS in the browser. Again, the hope is that once a lib supports CommonJS syntax it will never attach itself via a global on the window object. Which means you MUST use require()syntax and assign it to a local variable and then use it wherever you need it.

Browserify-shim 允许你通过 CommonJSrequire()语法指定一个你想要“公开”的全局变量。请记住,您可以做var whatever = global;var whatever = window.global;但不能做var whatever = require('global')并期望它为您提供正确的库/模块。不要对变量的名称感到困惑。它可以是任意的。您本质上是将全局变量设为局部变量。这听起来很愚蠢,但它是浏览器中 JS 的可悲状态。同样,希望一旦一个库支持 CommonJS 语法,它就永远不会通过 window 对象上的全局附加自身。这意味着您必须使用require()语法并将其分配给局部变量,然后在任何需要的地方使用它。

Note:I found variable naming slightly confusing in the browserify-shim docs/examples. Remember, the key is that you want to include a lib as ifit were a properly behaving CommonJS module. So what you end up doing is telling browserify that when you require myGlobal require('myGlobal')you actually just want to be given the global property on the window object which is window.myGlobal.

注意:我发现 browserify-shim 文档/示例中的变量命名有点混乱。请记住,关键是您希望包含一个库,就好像它是一个行为正常的 CommonJS 模块一样。因此,您最终要做的是告诉 browserify,当您需要 myGlobal 时,require('myGlobal')您实际上只想获得 window 对象的 global 属性,即window.myGlobal.

In fact, if you're curious as to what the require function actually does, it's pretty simple. Here's what happens under the hood:

事实上,如果您对 require 函数的实际作用感到好奇,那么它非常简单。这是幕后发生的事情:

var whatever = require('mygGlobal');

becomes...

变成……

var whatever = window.mygGlobal;

Exposing

暴露

So with that background, let's see how we expose a module/lib in our browserify-shim config. Basically, you tell browserify-shim two things. The name you want it accessible with when you call require()and the global it should expect to find on the window object. So here's where that global:*syntax comes in. Let's look at an example. I want to drop in jquery as a script tag in index.html so I get better performance. Here's what I'd need to do in my config (this would be in package.json or an external config JS file):

有了这个背景,让我们看看我们如何在我们的 browserify-shim 配置中公开一个模块/库。基本上,您告诉 browserify-shim 两件事。您希望在调用时使用的名称require()以及它应该在 window 对象上找到的全局名称。所以这就是global:*语法的用武之地。让我们看一个例子。我想在 index.html 中将 jquery 作为脚本标记放入,以便获得更好的性能。这是我需要在我的配置中做的事情(这将在 package.json 或外部配置 JS 文件中):

"browserify-shim": {
  "jquery": "global:$"
}

So here's what that means. I've included jQuery somewhere else (remember, browserify-shim has no idea where we put our tag, but it doesn't need to know), but all I want is to be given the $property on the window object when I require the module with the string parameter "jquery". To further illustrate. I could also have done this:

这就是这意味着什么。我已经在其他地方包含了 jQuery(请记住,browserify-shim 不知道我们把标签放在哪里,但它不需要知道),但我想要的只是$在我需要时在 window 对象上获得属性带有字符串参数“jquery”的模块。进一步说明。我也可以这样做:

"browserify-shim": {
  "thingy": "global:$"
}

In this case, I'd have to pass "thingy" as the parameter to the require function in order to get an instance of the jQuery object back (which it's just getting jQuery from window.$):

在这种情况下,我必须将“thingy”作为参数传递给 require 函数,以便返回 jQuery 对象的实例(它只是从中获取 jQuery window.$):

var $ = require('thingy');

And yes, again, the variable name could be anything. There's nothing special about $being the same as the global property $the actual jQuery library uses. Though it makes sense to use the same name to avoid confusion. This ends up referencing the the $property on the window object, as selected by the global:$value in the browserify-shimobject in package.json.

是的,变量名可以是任何东西。与实际 jQuery 库使用$的全局属性相同并没有什么特别之处$。尽管使用相同的名称以避免混淆是有意义的。这最终会引用$window 对象上的属性,由package.jsonglobal:$browserify-shim对象中的值选择。

Shimming

垫片

Ok, so that pretty much covers exposing. The other main feature of browserify-shim is shimming. So what's that? Shimming does essentially the same thing as exposing except rather than including the lib or module in HTML markup with something like a script tag, you tell browserify-shim where to grab the JS file locally. There's no need to use the global:*syntax. So let's refer back to our jQuery example, but this time suppose we are not loading jQuery from a CDN, but simply bundling it with all the JS files. So here's what the config would look like:

好的,所以几乎涵盖了暴露。browserify-shim 的另一个主要功能是填充。那是什么?Shimming 与公开的作用本质上是一样的,除了不是将 lib 或模块包含在 HTML 标记中并带有类似 script 标签的东西,你告诉 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": "$"
},

This config tells browserify-shim to load jQuery from the specified local path and then grab the $ property from the window object and return that when you require jQuery with a string parameter to the require function of "jquery". Again, for illustrative purposes, you can also rename this to anything else.

此配置告诉 browserify-shim 从指定的本地路径加载 jQuery,然后从 window 对象中获取 $ 属性,并在您需要带有字符串参数的 jQuery 时将其返回给“jquery”的 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": "$"
},

Which could be required with:

这可能需要:

var whatever = require('thingy');

I'd recommend checking out the browserify-shim docs for more info on the long-hand syntax using the exportsproperty and also the dependsproperty which allows you to tell browserify-shim if a lib depends on another lib/module. What I've explained here applies to both. I hope this helps others struggling to understand what browserify-shim is actually doing and how to use it.

我建议查看 browserify-shim 文档以获取有关使用该exports属性的长期语法的更多信息,以及depends允许您告诉 browserify-shim 一个库是否依赖于另一个库/模块的属性。我在这里解释的内容适用于两者。我希望这可以帮助其他努力了解 browserify-shim 实际在做什么以及如何使用它的人。

Anonymous Shimming

匿名匀场

Anonymous shimming is an alternative to browserify-shim which lets you transform libs like jQuery into UMD modules using browserify's --standaloneoption.

Anonymous shimming 是 browserify-shim 的替代方案,它允许您使用 browserify 的--standalone选项将jQuery 等库转换为 UMD 模块。

$ browserify ./src/js/vendor/jquery.js -s thingy > ../dist/jquery-UMD.js

If you dropped that into a script tag, this module would add jQuery onto the window object as thingy. Of course it could also be $or whatever you like.

如果你把它放到一个脚本标签中,这个模块会将 jQuery 添加到窗口对象中作为thingy. 当然它也可以是$或任何你喜欢的。

If however, it's requireed into your browserify'd app bundle, var $ = require("./dist/jquery-UMD.js");, you will have jQuery available inside the app without adding it to the window object.

但是,如果它被require编入浏览器化的应用程序包中var $ = require("./dist/jquery-UMD.js");,您将在应用程序中使用 jQuery,而无需将其添加到 window 对象。

This method doesn't require browserify-shim and exploits jQuery's CommonJS awareness where it looks for a moduleobject and passes a noGlobalflag into its factory which tells it not to attach itself to the window object.

此方法不需要 browserify-shim 并利用 jQuery 的 CommonJS 感知,在其中查找module对象并将noGlobal标志传递给它的工厂,该标志告诉它不要将自己附加到 window 对象。

回答by gottlike

For everyone, who is looking for a concrete example:

对于正在寻找具体示例的每个人:

The following is an example of package.jsonand app.jsfiles for a jQuery plugin that attaches itself to the jQuery/$object, e.g.: $('div').expose(). I don't want jQueryto be a global variable (window.jQuery) when I require it, that's why jQuery is set to 'exports': null. However, because the plugin is expecting a global jQueryobject to which it can attach itself, you have to specify it in the dependency after the filename: ./jquery-2.1.3.js:jQuery. Furthermore you need to actually export the jQueryglobal when using the plugin, even if you don't want to, because the plugin won't work otherwise (at least this particular one).

以下是将自身附加到/对象的 jQuery 插件的示例package.jsonapp.js文件,例如:. 当我需要它时,我不想成为一个全局变量 ( ),这就是为什么 jQuery 被设置为. 但是,因为插件需要一个全局对象,它可以将自身附加到该对象上,所以您必须在文件名后的依赖项中指定它:。此外,您需要在使用插件时实际导出全局变量,即使您不想这样做,因为否则插件将无法工作(至少是这个特定的)。jQuery$$('div').expose()jQuerywindow.jQuery'exports': nulljQuery./jquery-2.1.3.js:jQueryjQuery

package.json

包.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

应用程序.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();
  });
});

In the above example I didn't want my code to set any globals, but I temporarily had to do so, in order to make the plugin work. If you only need jQuery, you could just do this and don't need any workaround: var my_jQuery = require('./jquery-2.1.3.js'). If you are fine with your jQuery being exposed as a global, then you can modify the above package.jsonexample like so:

在上面的例子中,我不希望我的代码设置任何全局变量,但我暂时不得不这样做,以使插件工作。如果你只需要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" ] }

Hope that helps some people, who were looking for concrete examples (like I was, when I found this question).

希望能帮助一些正在寻找具体例子的人(就像我发现这个问题时一样)。

回答by Cool Blue

Just for completeness, here is a method that exploits jQuery's CommonJS awareness to avoid having to worry about polluting the windowobject without actually needing to shim.

为了完整起见,这里有一种方法可以利用 jQuery 的 CommonJS 意识来避免在window实际上不需要填充的情况下担心污染对象。

Features

特征

  1. jQuery included in the bundle
  2. plugin included in the bundle
  3. no pollution of the windowobject
  1. 捆绑包中包含的 jQuery
  2. 捆绑包中包含的插件
  3. 不污染物window

Config

配置

In ./package.json, add a browsernode to create aliases for the resource locations. This is purely for convenience, there is no need to actually shim anything because there is no communications between the module and the global space (script tags).

./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"
  }
}

Method

方法

  • Because jQuery is CommonJS-aware these days, it will sense the presence of the moduleobject provided by browserifyand return an instance, without adding it to the windowobject.
  • In the app, requirejquery and add it to the module.exportsobject (along with any other context that needs to be shared).
  • Add a single line at the start of the plugin to require the app to access the jQuery instance it created.
  • In the app, copy the jQuery instance to $and use jQuery with the plugin.
  • Browserify the app, with default options, and drop the resulting bundle into a script tag in your HTML.
  • 因为现在 jQuery 是 CommonJS-aware,它会感知browserifymodule提供的对象的存在并返回一个实例,而不会将它添加到对象中。window
  • 在应用程序中,requirejquery 并将其添加到module.exports对象(以及需要共享的任何其他上下文)。
  • 在插件的开头添加一行以要求应用程序访问它创建的 jQuery 实例。
  • 在应用程序中,将 jQuery 实例复制到插件中$并使用 jQuery。
  • 使用默认选项浏览应用程序,并将生成的包放入 HTML 中的脚本标记中。

Code

代码

app.cb.js

应用程序.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();
            })
    );
});

at the top of the plugin

在插件的顶部

var jQuery = require("app").jQuery;

in the HTML

在 HTML 中

<script type="text/javascript" src="app.cb.bundle.js"></script>

Background

背景

The pattern used by jQuery is to call it's factory with a noGlobalflag if it senses a CommonJS environment. It will not add an instance to the windowobject and will return an instance as always.

jQuery 使用的模式是,noGlobal如果它感知到 CommonJS 环境,就用一个标志调用它的工厂。它不会向window对象添加实例,并且会像往常一样返回一个实例。

The CommonJS context is created by browserifyby default. Below is an simplified extract from the bundle showing the jQuery module structure. I removed the code dealing with isomorphic handling of the windowobject for the sake of clarity.

CommonJS 上下文默认由browserify创建。下面是从显示 jQuery 模块结构的包中的一个简化摘录。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;
    }) );
}, {}]

The best method I found is to get things working in the node module system and then it will work every time after browserify-ing.
Just use jsdomto shim the windowobject so that the code is isomorphic. Then, just focus on getting it to work in node. Then, shim any traffic between the module and global space and finally browserify it and it will just work in the browser.

我发现的最好方法是让节点模块系统中的东西工作,然后每次浏览器化后它都会工作。
只需使用jsdom来填充window对象,以便代码是同构的。然后,只专注于让它在 node.js 中工作。然后,填充模块和全局空间之间的任何流量,最后浏览它,它只会在浏览器中工作。

回答by Jashwant

I was using wordpress. Hence, I was kind of forced to use the wordpress core's jQuery, available in windowobject.

我正在使用wordpress。因此,我被迫使用 wordpress 核心的 jQuery,在 object.js 中可用window

It was generating slick()not defined error, when I tried to use slick()plugin from npm. Adding browserify-shimdidn't help much.

slick()当我尝试使用slick()npm 中的插件时,它生成了未定义的错误。添加browserify-shim并没有太大帮助。

I did some digging and found out that require('jquery')was not consistent always.

我做了一些挖掘,发现这require('jquery')并不总是一致的。

In my theme javascript file, it was calling the wordpress core's jquery.

在我的主题 javascript 文件中,它调用了 wordpress 核心的 jquery。

But, in slickjquery plugin it was calling the latest jquery from node modules.

但是,在slickjquery 插件中,它从节点模块调用最新的 jquery。

Finally, I was able to solve it. So, sharing the package.jsonand gulpfileconfiguration.

最后,我能够解决它。所以,共享package.jsongulpfile配置。

package.json:

包.json:

"browserify": { "transform": [ "browserify-shim" ] }, "browserify-shim": { "jquery": "global:jQuery" },

"browserify": { "transform": [ "browserify-shim" ] }, "browserify-shim": { "jquery": "global:jQuery" },

gulpfile.babel.js:

gulpfile.babel.js:

browserify({entries: 'main.js', extensions: ['js'], debug: true}) .transform(babelify.configure({ presets: ["es2015"] })) .transform('browserify-shim', {global: true})

browserify({entries: 'main.js', extensions: ['js'], debug: true}) .transform(babelify.configure({ presets: ["es2015"] })) .transform('browserify-shim', {global: true})

Doing transform 'browserify-shim' was crucial part, I was missing earlier. Without it browserify-shimwas not consistent.

做转换 'browserify-shim' 是至关重要的部分,我之前错过了。没有它browserify-shim是不一致的。