Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Backbone.js with Require.js (r.js), but resulting in 2 files after I optimize it?

I have followed the basic tutorials (results in one file after you run r.js)

The problem is, my main.js file at the end is 500KB. That's too big. I want to split it into two files.

I want to optimize my main.js file into two files:

  1. One that holds the front page and user profile pages, since they're most accessed
  2. One that holds all the other pages (ordering, account settings, profile settings, etc.)

Most people will hit the front page and user profile pages, and I want those to load quickly first (while having the other pages load in the background in the 2nd main file)

The problem is, I don't know how to do this. There are examples like this online, but these examples do not use Backbone. They don't cover how to deal with router and app.js

I'm confused...because I only have one app.js, one router.js...how can I split router.js into two files?

I don't know how to split my project up when dealing with Backbone.

Below is the code

HTML PAGE (the entry point for my Single Page Application)

<html>
<head>
    <script type="text/javascript" data-main='/media/js/main' src='/media/js/lib/requirejs/require-jquery.js'></script>
</head>
<body>
    Hello
</body>
</html>

Main.js

require.config({
    paths:{
        jquery: 'lib/requirejs/require-jquery',
        jquery_ui:'lib/jquery-ui/jquery-ui-1.10.3.custom',
        underscore: 'lib/underscore/underscore-min',
        backbone:'lib/backbone/backbone-min',
        backbone_viewhelper:'lib/backbone/backbone.viewhelper',
        text: 'lib/requirejs/text',
        birthdaypicker: 'lib/birthdaypicker/bday-picker',
        //more paths
    },
    waitSeconds: 30,
    shim:{
        'underscore':{
            exports: '_'
        },
        'backbone':{
            deps:[ 'underscore', 'jquery'],
            exports: 'Backbone'
        },
        'backbone_viewhelper':{
            deps:['underscore','backbone']
        }
    }
});


require([
    'app',
    'json2',
    'jquery_ui',
    'backbone_viewhelper',
    'bootstrap_js',
    'bootstrap_select',
    'birthdaypicker',
    'accounting',
    'numbersonly',
    'main_alert',
    'string_tools',
    'plupload',
    //more things here
], function(App){
    App.initialize();
});

App.js

define([
    'jquery',
    'underscore',
    'backbone',
    'router'
], function($, _, Backbone, Router){    
    var initialize = function(){
        Router.initialize();
    }
    return {
        initialize: initialize
    };

});

Router.js

define([
    'jquery',
    'underscore',
    'backbone',
    'modules/index/view',
    'modules/home/view',
    'modules/listings_search/view',
    'modules/profile/view',
    //more modules
], function($, _, Backbone, indexView, homeView,searchView, profileView){
    var AppRouter = Backbone.Router.extend({
        initialize:function(){
            _.bindAll(this);
        },
        routes:{
            '':'index',
            'home': 'home',
            'register': 'register',
            'login': 'login',
            'listings(/start/:start)(/num/:num)': 'search',
            'listings/create': 'listingsCreate',
            'listings/:listing_id/edit': 'listingsEdit',
            'orders/listings/:listing_id/create': 'ordersCreate',
            'orders/buyer(/start/:start)(/num/:num)': 'ordersListBuyer',
            'orders/seller(/start/:start)(/num/:num)': 'ordersListSeller',
            'orders/:order_id': 'orders',
            'orders/:order_id/messages':'messages',
            '*actions': 'defaultAction'
            //more stuff
        },
        index:function(){
            app_router_view.show(indexView);
        },
        search:function(start, num){
            var options = {
                filters:{
                    start: start,
                    num: num
                }
            };
            app_router_view.show(searchView, options);
        },
        static:function(template){
            app_router_view.show(staticView, { static_view: { template: template }});
        },
        profile:function(){
            app_router_view.show(profileView);
        },
        passResetCode:function(code){
            app_router_view.show(passCodeView, {'code':code});
        },
        //more stuff
        home:function(){
            app_router_view.show(homeView);
        },
        defaultAction:function(actions){
            this.navigate('/', { trigger:true});
        }
    });
    var initialize = function(){
        var app_router = new AppRouter;
        Backbone.history.start({pushState:true, root: '/'});
        $(document).on('click', 'a:not([data-bypass])', function (evt) {
            var href = $(this).attr('href');
            if(href){
                var protocol = this.protocol + '//';
                if (href.slice(protocol.length) !== protocol && href != '#') {
                    evt.preventDefault();
                    app_router.navigate(href, { trigger: true});
                }
            }else{
            }
        });
    };
    return {
        initialize:initialize
    }
});

As you can see , my entire app starts with main.js, goes to app.js, and finally goes to router.js.

How can I split this?

like image 350
TIMEX Avatar asked Apr 02 '14 20:04

TIMEX


1 Answers

Based on the code you have shared, I've created a sample web-app and committed the code in git-hub.

Application is divided into 2 modules:

  • main : contains modules/index/view and modules/profile/view
  • other : contains 'modules/order/view and modules/search/view

When you request for modules/index/view or modules/profile/view, main.js is downloaded if not downloaded yet. Similarly when request is placed for modules/order/view or modules/search/view, other.js is downloaded if not downloaded yet. Remember to use require.js v2.1.10 or greater, as it has bundle feature which is required for generating other.js.

You can further modularize it by defining order, search, profile as independent modules in build.js, so that they are downloaded only when needed.

Output of executing build command :

media/js/main.js
----------------
media/js/lib/jquery/jquery-min.js
media/js/lib/underscore/underscore-min.js
media/js/lib/backbone/backbone-min.js
media/js/router.js
media/js/app.js
media/js/main.js
media/js/modules/index/model.js
media/js/modules/index/view.js
media/js/modules/profile/model.js
media/js/modules/profile/view.js

media/js/other.js
----------------
media/js/modules/order/model.js
media/js/modules/order/view.js
media/js/modules/search/model.js
media/js/modules/search/view.js

The execution flow goes like this: index.html => media/js/main [it has index/view, profile/view, app.js and all the dependencies]. By default Index view is shown, as it is configured for home route.

When Profile links is clicked, no more files are downloaded, as main.js is already downloaded. When Search / Order links are clicked, other.js is downloaded.

like image 184
Manish Mulimani Avatar answered Sep 25 '22 23:09

Manish Mulimani