javascriptrequirejsrequirejs-optimizer

RequireJS: Optimized files and older browsers


I am trying to determine the best way to have a single minified file, but also support older browsers to an extent. I have a large web application that is using a multitude of 3rd party libraries that break IE9 and below. Ideally I would like to support these browsers at least to the point of detecting them and showing the user a sub-view, asking them to upgrade. My RequireJS flow is as follows:

In my HTML, I have a back-end conditional statement that will either output the first link on the chain of loading (my config file), or, the minified payload (if on a production server):

<?php
    if (env('APP_ENV') === 'dev') {
        echo $this->Html->script('libs/requirejs/require', ['data-main' => '/js/src/config.r']);
    } else {
        echo $this->Html->script('build.min');
    }
?>

config.r.js looks like this:

require.config({
    baseUrl: '/js',
    deps: ['src/boot'],
    paths: {
        'requireJS': '../bower_components/requirejs/require',
        'requireDomReady': '../bower_components/requirejs-domready/domReady',
        'handlebars': '../bower_components/handlebars/handlebars.runtime.min',
        'templates': 'src/templates',
        'jquery': '../bower_components/jquery/dist/jquery.min',
        'backbone': '../bower_components/backbone/backbone',
        'underscore': '../bower_components/underscore/underscore',
    },
    shim: {
        'handlebars': {
            exports: 'Handlebars'
        },
        'underscore': {
            exports: '_'
        },
        'backbone': {
            deps: ['handlebars', 'underscore'],
            exports: 'Backbone'
        }
    }
});

boot.js looks like this:

define([
    'requireDomReady',
    'src/utils/userAgent'
], function(
    requireDomReady,
    userAgent
) {
    'use strict';

    // If we have detected that this browser isnt supported, show the appropriate screen.
    if (userAgent.isIE9 || userAgent.isIE8) {
        var upgradeScreen = document.getElementById('upgrade-screen');
        upgradeScreen.style.display = 'block';
        return;
    }

    // With the inital check of support being completed, load the main app.
    require([
        'src/app'
    ], function(
        app
    ) {
        requireDomReady(app);
    });
});

The minfied payload basically merges config.r.js, boot.js and app.js along with all of its 3rd party librariess. Whenever the minified payload is served on production with all of the libraries inside of it, they're insta-parsed which makes IE9 and below insta-error just at their mere presence.

How could I split this up so this boot sequence is separate from the app sequence, while still loading either the minified files or the loose individual files? The part I'm getting hung up on is how I could do this but ask it to either require my config.r.js, or, my minified script file depending on my environment.

Any guidance at all would be appreciated. I don't want to have to downgrade libraries or add polyfills just to get rid of errors and show an upgrade view..


Solution

  • Any code that causes a parse error will need to be in a different file to your config and boot.

    You could put the browser check in the config file and make it add different paths if it is IE.

    There is also the IE conditional html comments which can be used to load different files.

    This is how I use requirejs to load my shims and polyfills only if they are required by the browser.

    //main
    require.config({
        paths: {
            "JSON": "shims/json2",
            "Array": "shims/array",
            "input.placeholder": "shims/placeholders.min"
        },
        shim: {
            "JSON": {
                exports: "JSON"
            }
        }
    });
    
    if (typeof JSON === 'object') {
        define('JSON', [], JSON);
    } else {
        //load shim for JSON now
        require(['JSON']);
    }
    
    if (Array.prototype.forEach) {
        //if forEach exists assume all new array functions exist
        define('Array', [], function () { return Array; });
    }
    
    if ('placeholder' in document.createElement('input')) {
        define('input.placeholder');
    }
    

    The shim/polyfill file then either includes a define or it is in require.config.shim like JSON.

    //shims/array.js
    //Add new array functions to prototype.
    if(typeof define==='function'&&define.amd){define('Array',[],function(){return Array});}
    

    Then if I need that feature I include the dependency.

    require(['src/app','JSON','Array','input.placeholder'], function (app, JSON) {
        //can now use [].forEach() without checking if it exists
    });