Search code examples
node.jsbrowserify

What is the difference between browserify external vs. exclude?


I'm using browserify and trying to get it to skip wasting time including or parsing jquery and other require-less files I've loaded via CDN.

Should I use bundle.exclude('jquery') or bundle.external('jquery')? What is the difference? Their output seemed identical, and the docs are unclear to me:

Prevent file from being loaded into the current bundle, instead referencing from another bundle.

If file is an array, each item in file will be externalized.

If file is another bundle, that bundle's contents will be read and excluded from the current bundle as the bundle in file gets bundled.

Prevent the module name or file at file from showing up in the output bundle.

If your code tries to require() that file it will throw unless you've provided another mechanism for loading it.


Solution

  • Answer:

    You should use exclude.

    Explanation:

    Both functions prevent the file from being included in the bundle. For your use case, you're probably not going to require jQuery, so it really doesn't matter which you use. However, this is what's going on:

    browserify uses module-deps to explore your code and find any require statements, and then tells module-deps where to find the required module.

    If the file is in the bundle, it simply needs to provide the key for it in the bundle's module map.

    If you said the file is external, the browserify assumes you mean it's included in another bundle, and so provides a path to the file assuming that that as an id will resolve from anther bundle. There is a little additional bookkeeping involved to be able to do this.

    If you exclude the file, then browserify will provide undefined to module-deps and there will definitely be a fire when you try to use the bundle that requiring said file. However, this approach lacks the overhead of tracking the file path (which would really be negligible) and doesn't "waste time" looking in other bundles before exploding.

    Some examples: I fiddled with node-browserify/example/api to produce some bundles and the examples below are the module maps from various tests, somewhat formatted for readability.

    Vanilla - ran as it is in the browserify repo:

    {
        1: [function(require, module, exports) {
            module.exports = function(n) {
                return n * 3 };
    
        }, {}],
        2: [function(require, module, exports) {
            var bar = require('./bar');
    
            module.exports = function(n) {
                return n * bar(n);
            };
    
        }, { "./bar": 1 }],
        3: [function(require, module, exports) {
            var foo = require('./foo');
            console.log(foo(5));
    
        }, { "./foo": 2 }]
    }
    

    3 (main.js) depends on ./foo which is at 2

    2 (foo.js) depends on ./bar which is at 1

    1 (bar.js) has no dependencies

    Marked api/bar.js as external:

    {
        1: [function(require, module, exports) {
            var bar = require('./bar');
    
            module.exports = function(n) {
                return n * bar(n);
            };
    
        }, { "./bar": "/browser/bar.js" }],
        2: [function(require, module, exports) {
            var foo = require('./foo');
            console.log(foo(5));
    
        }, { "./foo": 1 }]
    }
    

    2 (main.js) depends on ./foo which is at 1

    1 (foo.js) depends on ./bar which should be labelled /browser/bar.js in some other bundle

    Marked api/bar.js to exclude:

    {
        1: [function(require, module, exports) {
            var bar = require('./bar');
    
            module.exports = function(n) {
                return n * bar(n);
            };
    
        }, { "./bar": undefined }],
        2: [function(require, module, exports) {
            var foo = require('./foo');
            console.log(foo(5));
    
        }, { "./foo": 1 }]
    }
    

    2 (main.js) depends on ./foo which is at 1

    1 (foo.js) depends on ./bar which is at ZOMFG! I dunno where it is. yru require??!1!

    Removed the exclude/external call, and removed the require of ./bar from foo.js:

    {
        1: [function(require, module, exports) {
            module.exports = function(n) {
                return n * bar(n);
            };
    
        }, {}],
        2: [function(require, module, exports) {
            var foo = require('./foo');
            console.log(foo(5));
    
        }, { "./foo": 1 }]
    }
    

    2 (main.js) depends on ./foo which is at 1

    1 (foo.js) has no dependencies, the world is peachy. I wonder if they loaded bar by some other means