How to use my own version of jQuery with browserif

2019-02-28 15:40发布

问题:

(I should clarify up front: My question is about closures and client module patterns in Javascript. It's not about how to use jQuery.noConflict().)

I've got a bit of Javascript that people can add to their websites. I want my own code to have access to a $ variable which resolves to a specific version of jQuery that's independent of whatever the page has loaded. This is easy if all my code is in a single file, inside a closure that I define. But I'm struggling to find a clean way to do this using a module pattern where my code is in separate closures.

Background (i.e. the obvious thing which isn't the problem)

This was easy when all my code was in one file. I could just create a $ var within my outermost closure and use $.noConflict(...) to make sure the outer page kept its own jQuery version. Something like this:

// This is easy and works as you'd expect
(function() {

    var $; // The $ var in my local scope that the rest of my code can use.

    function loadMyVersionOfjQuery() {
        insertTheAppropriateScriptTagAndWaitForTheScriptToLoad(function() {
            // Set the $ in my local scope and restore the global jQuery.
            $ = jQuery.noConflict(true);
        }
    }

    loadMyVersionOfjQuery();

    ... etc. ...
})();

Now I'm in the process of breaking my code up into separate files using browserify and this is no longer so easy. The most trouble comes from the fact that my version of jQuery is loaded asynchronously. So at the time my module requires() are being processed my version of jQuery isn't ready. This prevents me from simply creating and assigning a $ var at the top level of my module closures.

Some ideas I've looked into

  1. If I could defer the initialization of my modules until after jQuery is run, then each of my modules could define their own $ var. But this doesn't seem possible. It looks like my requires(...) calls are aggressively traversed even if I try to hide them inside function callbacks (browserify seems to actually parse the JS to find the require statements).
  2. If I could define a dynamic proxy object, I could initialize my own $ vars with a proxy that would later delegate to my loaded version of jQuery. But Javascript doesn't support the dynamic proxy pattern.
  3. Browserify actually defines a closure that wraps all of my modules when they're "transpiled" to a single file. If I could somehow give it a snippet of code to insert into this closure (simply "var $;"), I'd be in business. But I haven't found any way to do this.
  4. As a last resort, it occurred to me that I could create my own closure around the browserify output and define the scoped variable I need there. By either processing the output file or by doing a hacky concatenation of a couple simple files around the browserify content (something like "(function(){var $;" + bundle.js + "})();". But this is just too hacky.

Does anyone have any ideas? How do you develop client Javascript modules in multiple files but still get a closure for all your code together?

回答1:

It could be that I'm the only person who ever gets into this situation (Browserify + an asynchronously loaded library that I want to use in all my modules), but I'll share the workaround that I've come up with just in case...

I ended up defining a module that asynchronously loads jQuery and then notifies listeners when it's ready. It's basically a very simple support for asynchronous 'requires'. All of my modules that want to use jQuery then end up with a small bit of boilerplate code like this:

var $; require('./jquery-provider').onLoad(function(jQuery) { $=jQuery; });

It's not perfect but it's simple. And it works because the entry point of my library kicks off my 'jQuery provider' and waits for a ready callback before it calls to all my other modules. So although my modules all get aggressively executed by Browserify as it resolves all the dependencies, none of the functions inside my modules get run until my required library is available and has been passed around to them.

(If this pattern is useful to anyone else, I can share more code.)



回答2:

in my app.js I have this

var $ = require('jquery')(window);  global.jQuery = require("jquery");

and then use download from npm "plugin" and import de module and execute, like this.

var plugin    = require('plugin');
plugin();

and works fine.