2
votes

We have built a huge backbone-marionette application with a lot of different libraries (bootstrap, gmaps, momentjs etc.). Everything works like charm when I combine everything into a single even in production mode. To improve the performance most of the libraries should be loaded from a CDN now.

But this seems not be as easy as expected. I started with this great tutorial (http://tech.pro/blog/1639/using-rjs-to-optimize-your-requirejs-project) and added a infrastructure module which is responsible to load the external libs.

infrastructure.js

define([
    'jquery'
], function($) {
    'use strict';
});

main.js (it has grown in the last few months a bit :D)

require.config({
    urlArgs: 'bust=1386581060770',
    paths: {
        // jquery: '../../bower_components/jquery/dist/jquery',
        jquery: '//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min',
        underscore: '../../bower_components/underscore/underscore',
        marionette: '../../bower_components/marionette/lib/backbone.marionette',
        backbone: '../../bower_components/backbone/backbone',
        moment: '../../bower_components/moment/moment',
        'moment.de': '../../bower_components/moment/lang/de',
        text: '../../bower_components/text/text',
        'backbone.modelbinder': '../../bower_components/backbone.modelbinder/Backbone.ModelBinder',
        'backbone.analytics': '../../bower_components/backbone.analytics/backbone.analytics',
        'requirejs-i18n': '../../bower_components/requirejs-i18n/i18n',
        'jquery.cookie': '../../bower_components/jquery.cookie/jquery.cookie',
        'jquery.simplePagination': '../../bower_components/jquery.simplePagination/jquery.simplePagination',
        'underscore.string': '../../bower_components/underscore.string/dist/underscore.string.min',
        parsleyjs: '../../bower_components/parsleyjs/dist/parsley',
        'parsleyjs-de': '../../bower_components/parsleyjs/src/i18n/de',
        'parsleyjs-comparison': '../../bower_components/parsleyjs/src/extra/validator/comparison',
        requirejs: '../../bower_components/requirejs/require',
        'underscore.deepExtend': '../../vendor/others/tools/underscore.mixin.deepExtend',
        'underscore.templatehelper': '../../vendor/others/tools/underscore.mixin.templateHelper',
        templates: '../templates',
        infrastructure: 'infrastructure',
        confighandler: 'config/confighandler',
        layout: 'views/layout',
        general: 'general',
        helper: 'helper',
        vent: 'vent',
        view: 'views',
        model: 'models',
        module: 'modules',
        behaviors: 'behaviors',
        collection: 'collections',
        controller: 'controllers',
        nls: 'nls',
        'bootstrap-select': '../../bower_components/bootstrap-select/bootstrap-select',
        'eonasdan-bootstrap-datetimepicker': '../../bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min',
        'eonasdan-bootstrap-datetimepicker.de': '../../bower_components/eonasdan-bootstrap-datetimepicker/src/js/locales/bootstrap-datetimepicker.de',
        'bootstrap': '../../bower_components/bootstrap/dist/js/bootstrap',
        'jquery-cropbox': '../../bower_components/jquery-cropbox/jquery.cropbox',
        'jquery-geocomplete': '../../bower_components/jquery-geocomplete/jquery.geocomplete',
        'seiyria-bootstrap-slider': '../../bower_components/seiyria-bootstrap-slider/js/bootstrap-slider',
        'leaflet': '../../vendor/leaflet/leaflet-custom',
        modernizr: '../../bower_components/modernizr/modernizr',
        intro: '../../bower_components/intro.js/intro',
        lightbox2: '../../bower_components/lightbox2/js/lightbox'
    },
    shim: {
        'underscore.deepExtend': 'underscore',
        'underscore.string': 'underscore',
        'parsleyjs': 'jquery',
        'eonasdan-bootstrap-datetimepicker': 'moment',
        'parsleyjs-comparison': 'parsleyjs',
        'parsleyjs-de': 'parsleyjs',
        'select2-de': 'select2',
        'lightbox2': 'jquery',
        'jquery.simplePagination': 'jquery',
        'bootstrap-select': 'bootstrap',
        'bootstrap-slider': 'bootstrap',
        'backbone.modelbinder': 'backbone',
        underscore: {
            deps: ['jquery'],
            exports: '_'
        },
        backbone: {
            deps: [
                'underscore',
                'jquery'
            ],
            exports: 'Backbone'
        },

        'backbone.analytics': {
            deps: [
                'underscore',
                'backbone'
            ],
            exports: 'Backbone.Analytics'
        },
        'backbone.deepmodel': {
            deps: [
                'underscore',
                'backbone'
            ]
        },
        marionette: {
            deps: [
                'backbone'
            ],
            exports: 'Backbone.Marionette'
        },
        leaflet: {
            exports: 'L'
        },
        'jquery-geocomplete': {
            deps: [
                'jquery',
                'general/googlemaps'
            ]
        }
    }
});

require(['infrastructure'], function() {
    'use strict';
    console.log('infrastructure');

    require(['app', 'model/session'], function(App, Session) {

        console.log('app, model/session');
        App.session = Session;

        App.session.login(function() {
            App.start();
        });
    });
});

the build.js part included in my grunt configuration

requirejs: {
  compile: {
    options: {
      mainConfigFile: 'app/scripts/main.js',
      paths: {
        jquery: 'empty:'
      },
      modules: [{
        name: 'main',
        exclude: [
          'infrastructure'
        ]
      }, {
        name: 'infrastructure'
      }],
      findNestedDependencies: true,
      preserveLicenseComments: false,
      stubModules: ['text'],
      inlineText: true,
      optimize: 'none',
      dir: '<%= build.dest %>/app/scripts/',
      wrapShim: true
    }
  }
}

On the first step I only wanted to include jquery from a CDN. After the build process I've got two files a main.js and the infrastrucute.js. But if I try to run the application that error message is thrown in the console:

 Uncaught Error: Bootstrap's JavaScript requires jQuery 

I thought this problem wont occur, because the app requires the manufacture-module before all other parts of the application and the dependent libraries were loaded in the main.js. But it seems that all dependencies within main.js are still being loaded before the manufacture-module.

At the moment I'm a bit confused and don't know how to go on or even where to start.

Does anybody has already optimized apps from that size using r.js with libs from CDN. In addition what do you think about the concatenating/minification process + loading libs from a CDN at all? Does it boost the loading performance really?

thx in advance

1

1 Answers

1
votes

Add a dependancy on JQuery for Bootstrap. When JQuery was loaded from your server, it is loaded fast enough to be available when Bootstrap starts. Now that JQuery comes from a CDN, it takes more time to be loaded, and is not available when Bootstrap starts.