Как использовать Backbone.js с Require.js(r.js), но в результате получается 2 файла после его оптимизации?

Я следил за основными учебниками (результаты в одном файле после запуска r.js)

Проблема в том, что мой файл main.js в конце - 500 КБ. Это слишком большой. Я хочу разбить его на два файла.

Я хочу оптимизировать мой файл main.js в два файла:

  • Тот, который содержит страницы главной страницы и профиля пользователя, поскольку они наиболее доступны
  • Тот, который содержит все остальные страницы (заказы, настройки учетной записи, настройки профиля и т.д.).

Большинство людей попадут на страницы главной страницы и профиля пользователя, и я хочу, чтобы они сначала загружались быстро (при загрузке других страниц в фоновом режиме во втором главном файле)

Проблема в том, что я не знаю, как это сделать. Есть примеры, подобные этому онлайн, но эти примеры не используют Backbone. Они не описывают, как обращаться с маршрутизатором и app.js

Я запутался... потому что у меня есть только один app.js, один router.js... как я могу разбить router.js на два файла?

Я не знаю, как разделить мой проект при работе с Backbone.

Ниже приведен код

HTML-страница (точка входа для моего приложения с одной страницей)

<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
    }
});

Как вы можете видеть, все мое приложение начинается с main.js, переходит в app.js и, наконец, переходит в router.js.

Как я могу разделить это?

Ответ 1

На основе кода, которым вы поделились, я создал образец веб-приложения и передал код git -hub.

Приложение разделено на 2 модуля:

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

Когда вы запрашиваете modules/index/view или modules/profile/view, main.js загружается, если он еще не загружен. Аналогично, когда запрос помещается для modules/order/view или modules/search/view, other.js загружается, если он еще не загружен. Не забудьте использовать require.js v2.1.10 или больше, поскольку у него есть функция связки, которая требуется для генерации other.js.

Вы можете дополнительно модулировать его, указав order, search, profile в качестве независимых модулей в build.js, чтобы они загружались только при необходимости.

Результат выполнения команды сборки:

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

Поток выполнения выглядит следующим образом: index.html => media/js/main [имеет index/view, profile/view, app.js и все зависимости]. По умолчанию отображается представление индекса, так как оно настроено для домашнего маршрута.

При щелчке по ссылкам профиля больше не загружается больше файлов, так как main.js уже загружен. При щелчке по ссылкам поиска/заказа загружается other.js.

Ответ 2

Я создал пример, чтобы показать, как это можно сделать. Он содержит скелет приложения Backbone. Приложение там разделено на:

  • a main, который содержит ядро ​​приложения и только отображает "основное" представление (здесь views/app),

  • и secondary, который содержит все другие виды.

Пакет secondary загружается только по мере необходимости. В этом приложении это означает, что он должен быть загружен только тогда, когда используются представления foo или bar, а не раньше. (Это можно проверить, просмотрев сетевые операции в вашем браузере.)

Ключевыми моментами являются:

  • Вид в views/app - это "главный" вид выражение. Загрузка его создает и отображает право просмотра прочь.

  • Модуль js/router не использует другие представления напрямую. Это вызывает require, чтобы сначала загрузить представление. Это делает foo и bar функция асинхронная.

  • Это часть файла build.js, который делит приложение в два пучка:

    modules: [
        {
            name: "main"
        },
        {
            name: "secondary",
            // There no module named secondary in the source, so create it.
            create: true,
            // Make sure to include all the views other than the main one.
            include: [
                "views/foo",
                "views/bar"
            ],
            // Exclude everything we've included in `main`.
            exclude: ["main"]
        }
    ]
    
  • Оптимизированная версия нуждается в начальной конфигурации, например:

      bundles: {
         "secondary": ["views/foo", "views/bar"]
      }
    

    Это говорит RequireJS, что модули views/foo и views/bar загружаются загрузкой secondary.

Подробнее читайте в файле README.md.

Ответ 3

В профиле сборки вы можете указать, сколько модулей вы хотите создать и какие зависимости они должны включать (см. http://requirejs.org/docs/optimization.html#basics).

Здесь вы можете увидеть параметры https://github.com/jrburke/r.js/blob/master/build/example.build.js, интересующая вас часть начинается в строке 350.

Также см. Как использовать профиль сборки RequireJS + r.js в многостраничном проекте для более глубокого ответа

Ответ 4

Разделение кода на два файла - это не путь.

Разделение кода на два файла приведет только к увеличению времени загрузки в целом, поскольку потребуется дополнительный код оболочки. Кроме того, разделение кода может излишне увеличивать модульность кода, вызывая проблемы проектирования в будущем.

Путь к переходу должен заключаться в ленивом загрузке маршрутизируемых файлов (поэтому сначала загружайте страницы с первой страницы и профиля пользователя), а затем вы можете загружать другие файлы в фоновом режиме после того, как вы выполнили загрузку фронта страницы и страницы профиля пользователя.

Можно разбить его - не поймите меня неправильно, но при этом просто создайте ненужный код и действительно не поможет.

Здесь ссылка, которая может помочь.