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

时间:2014-07-19 01:41:03

标签: javascript configuration browserify

我正在使用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对象上)?

4 个答案:

答案 0 :(得分:97)

在重新审视并尝试了更多的东西之后,我终于围绕着浏览器shim正在做什么以及如何使用它。对我来说,在我最终了解如何使用browserify-shim之前,我必须掌握一个关键原则。对于两种不同的用例,有两种方法可以使用browserify-shim:exposing&垫补。

背景

我们假设您只想在标记中插入脚本标记(出于测试或性能原因,如缓存,CDN等)。通过在标记中包含脚本标记,浏览器将点击脚本,运行它,并且很可能在窗口对象上附加属性(在JS中也称为全局)。当然,这可以通过myGlobalwindow.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.jsonapp.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对象而不需要实际需要填充。

功能

  1. 包含在包中的jQuery
  2. 插件中包含的插件
  3. 没有污染window对象
  4. 配置

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

    方法

    • 因为jQuery现在可以识别CommonJS,它会感知 browserify 提供的module对象的存在并返回一个实例,而不会将其添加到window对象。
    • 在应用程序中,require jquery并将其添加到module.exports对象(以及需要共享的任何其他上下文)。
    • 在插件的开头添加一行,要求应用程序访问它创建的jQuery实例。
    • 在应用程序中,将jQuery实例复制到$并在插件中使用jQuery。
    • 使用默认选项对应用进行浏览化,然后将生成的包放入HTML中的脚本标记中。

    代码

    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.jsongulpfile配置。

<强>的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不一致。