Backbone Marionette和RequireJS模块

16

我正在使用Marionette开始一个大规模的JavaScript应用程序。Marionette应用程序具有应用程序模块的概念,并且还使用RequireJS将代码分解为模块。

目前,我在我的应用程序开头有以下内容:

require([ "jquery", "underscore", "backbone", "marionette" ],
function ($, _, Backbone, Marionette) {
    $(function() {

        App = new Marionette.Application();
        App.addInitializer(function(options) {
            App.addRegions({
                mainArea: "#mainArea"
            });
        });

        App.on("start", function() {
            // done starting up, do stuff here
        });

        App.start();
    });
});

如果我想添加一个视图,我需要在文件中做类似以下的操作吗?

require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {

    App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
        return Marionette.ItemView.extend({
            //define view stuff in here
         });
    });

});

我不确定如何运行这段代码,请帮忙指导,非常感谢。

2个回答

23

1
我不建议使用多个脚本标签。BBCloneMail并不是正确使用的例子。:) 真正的项目有构建步骤来合并和压缩代码。r.js可以用于requirejs模块,或者可以使用其他许多工具,如Rails资产管道、grunt.js等。 - Derick Bailey
2
这是一个使用RequireJS的Marionette更新链接。(Github存储库已移动。)https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs - Thaddeus Albers
1
现在我可以使用这个答案作为参数,因为它来自MarionetteJS的创建者。 :) - Eugene Gluhotorenko
3
使用Marionette模块和RequireJS仍然有好处吗?比如能够停止模块,从而释放内存的能力。 - James
5
AMD模块与Marionette模块的维基百科文章仍存在于任何地方吗?我在网上看到了许多对它的引用,但链接已不再有效。 - Choy
显示剩余4条评论

4

在我看来,与上述观点“Marionette的模块旨在成为RequireJS(和其他)模块格式的简单替代品”不同。

我喜欢用C#的程序集和命名空间概念将Require.js模块和Marionette.js模块进行比较。 Marionette.js的模块帮助我们根据功能将各种构建块的定义分组,而Require.js可以用于加载/注入依赖项。

再次强调,这是我的观点/理解(基于与David Sulc在他的书“使用RequireJS和Marionette模块构造Backbone代码”的讨论),这有助于我的实现。在某种程度上,我们可以像下面描述的那样同时使用Marionette.js和Require.js。

以下示例是一个小型的图书管理应用程序(示例),可以在线找到@ https://github.com/srihari-sridharan/LibraryManagement。下面的代码(省略了不重要的部分)创建应用程序对象并在初始化后呈现书籍列表。请在此处找到它 - https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/app.js

define([
    'marionette',
    'modules/config/marionette/regions/dialog'], function (Marionette) {

    // Create the application object
    var LibraryManager = new Marionette.Application();

    // Add regions to the application object
    LibraryManager.addRegions({
        //Header
        headerRegion: "#header-region",
        //Main
        mainRegion: "#main-region",
        //Footer
        footerRegion: "footer-region",
        //Overlay Dialog
        dialogRegion: Marionette.Region.Dialog.extend({
            el:"#dialog-region"
        })
    });

    // Subscribe to Initialize After event.
    LibraryManager.on('initialize:after', function() {
        if(Backbone.history){
            require(['modules/books/booksModule', 'modules/about/aboutModule'], function (){
                Backbone.history.start();    
                if(LibraryManager.getCurrentRoute() === ''){
                    LibraryManager.trigger("books:list");
                }                    
            });
        }
    });

    // Return the application object.
    return LibraryManager;
});

下一步,我们根据功能定义模块/子模块。这也将有一个特定于模块的路由器,并将连接控制器并处理路由。请注意对控制器的 require 调用。此代码位于 https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/booksModule.js
define(['app'], function (LibraryManager) {
    // Define a new module for Books - BooksModule
    LibraryManager.module('BooksModule', function (BooksModule, LibraryManager, Backbone, Marionette, $, _) {

        BooksModule.startWithParent = false;

        BooksModule.onStart = function () {
            console.log('Starting BooksModule.');
        };

        BooksModule.onStop = function () {
            console.log('Stopping BooksModule.');
        };

    });

    // Define a new module for a Router specific to BooksModule
    LibraryManager.module('Routers.BooksModule', function (BooksModuleRouter, LibraryManager, Backbone, Marionette, $, _) {

        BooksModuleRouter.Router = Marionette.AppRouter.extend({
            appRoutes: {
                'books': 'listBooks',
                'books(?filter:=criterion)': 'listBooks',
                'books/:id': 'showBook',
                'books/:id/edit': 'editBook'
            }
        });

        var executeAction = function (action, arg) {
            LibraryManager.startSubModule('BooksModule');
            action(arg);
            LibraryManager.execute('set:active:header', 'books');
        };

        var API = {
            // This is where we are using / referring to our controller
            listBooks: function (criterion) {
                require(['modules/books/list/listController'], function (ListController) {
                    executeAction(ListController.listBooks, criterion);
                });
            },

            showBook: function (id) {
                require(['modules/books/show/showController'], function (ShowController){
                    executeAction(ShowController.showBook, id);
                });
            },

            editBook: function (id) {
                require(['modules/books/edit/editController'], function (EditController) {
                    executeAction(EditController.editBook, id);
                });
            }

        };

        // Navigating routes.
        LibraryManager.on('books:list', function () {
            LibraryManager.navigate('books');
            API.listBooks();
        });

        LibraryManager.on('books:filter', function(criterion) {
            if(criterion){
                LibraryManager.navigate('books?filter=' + criterion);
            }
            else{
                LibraryManager.navigate('books');
            }
        });

        LibraryManager.on('book:show', function (id) {
            LibraryManager.navigate('books/' + id);
            API.showBook(id);
        });

        LibraryManager.on("book:edit", function(id){
            LibraryManager.navigate('books/' + id + '/edit');
            API.editBook(id);
        });

        LibraryManager.addInitializer(function () {
            new BooksModuleRouter.Router({
                controller: API
            });
        });
    });

    return LibraryManager.BooksModuleRouter;
});

最终我们有了视图、模型和控制器的定义。这些定义将与模块/子模块对象相关联。
以下是视图代码。请注意 .extend() 方法。它们被分配给附加到 BooksModule.List.View 子模块的变量。https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/list/listView.js
define(['app',
        'tpl!modules/books/list/templates/layout.html',
        'tpl!modules/books/list/templates/panel.html',
        'tpl!modules/books/list/templates/none.html',
        'tpl!modules/books/list/templates/list.html',
        'tpl!modules/books/list/templates/listItem.html'], 
    function (LibraryManager, layoutTemplate, panelTemplate, noneTemplate, listTemplate, listItemTemplate) {

        LibraryManager.module('BooksModule.List.View', function(View, LibraryManager, Backbone, Marionette, $, _) {

            View.Layout = Marionette.Layout.extend({

                template: layoutTemplate,

                regions:{
                    panelRegion: '#panel-region',
                    booksRegion: '#books-region'
                }

            });

            View.Panel = Marionette.ItemView.extend({
                // More code here!
            });

            View.Book = Marionette.ItemView.extend({                
                // More code here!
            });

            var NoBooksView = Marionette.ItemView.extend({
                template: noneTemplate,
                tagName: "tr",
                className: "alert"
            });

            View.Books = Marionette.CompositeView.extend({
                // More code here!
            });
        });
    return LibraryManager.BooksModule.List.View; // Return the definition.
});

控制器代码如下所示。这是从booksModule.js中的代码调用的。控制器定义附加到BooksModule.List子模块。
define(['app', 'modules/books/list/listView'], function (LibraryManager, View) {

    LibraryManager.module('BooksModule.List', function (List, LibraryManager, Backbone, Marionette, $, _) {

        List.Controller = {

            listBooks: function (criterion) {

                require(['common/views', 'entities/book'], function (CommonViews) {

                    var loadingView = new CommonViews.Loading();
                    LibraryManager.mainRegion.show(loadingView);

                    var fetchingBooks = LibraryManager.request('book:entities');
                    var booksListLayout = new View.Layout();
                    var booksListPanel = new View.Panel();

                    require(['entities/common'], function (FilteredCollection) {

                        $.when(fetchingBooks).done(function (books) {
                            // More code here!
                            });

                            if(criterion){
                                filteredBooks.filter(criterion);
                                booksListPanel.once('show', function () {
                                    booksListPanel.triggerMethod("set:filter:criterion", criterion);
                                });
                            }

                            var booksListView = new View.Books({
                                collection: filteredBooks
                            });

                            booksListPanel.on('books:filter', function (filterCriterion) {
                                filteredBooks.filter(filterCriterion);
                                LibraryManager.trigger("books:filter", filterCriterion);
                            });

                            booksListLayout.on("show", function(){
                                booksListLayout.panelRegion.show(booksListPanel);
                                booksListLayout.booksRegion.show(booksListView);
                            });

                            booksListPanel.on('book:new', function () {

                                require(["modules/books/new/newView"], function (NewView) {
                                        // More code here!
                                    });

                                    LibraryManager.dialogRegion.show(view);
                                });
                            });

                            booksListView.on('itemview:book:show', function (childView, model) {
                                LibraryManager.trigger("book:show", model.get('id'));
                            });

                            booksListView.on('itemview:book:edit', function(childView, model) {
                                require(['modules/books/edit/editView'], function (EditView) {
                                    // More code here!
                                    LibraryManager.dialogRegion.show(view);
                                });
                            });

                            booksListView.on("itemview:book:delete", function (childView, model) {
                                model.destroy();
                            });

                            LibraryManager.mainRegion.show(booksListLayout);

                        });

                    });

                });

            }

        }

    });

    return LibraryManager.BooksModule.List.Controller; // Return the definition.
});

因此,require.js模块和marionette模块可以共存。以下是它们的优点:
  • 源代码更清晰组织,关注点更明确分离。
  • 模块启动和停止方法提供初始化和清理对象的功能。
  • 当将功能和子功能建模为模块和子模块时,我们可以更细粒度地控制内存中的内容。
  • 此外,模块定义可以拆分成多个文件。

请发表您的想法。感谢阅读。

PS:根据上述观点,请查看下面示例的更改:

require([ "jquery", "underscore", "backbone", "marionette" ],
function($, _, Backbone, Marionette) {
    App.module("FirstView", function(FirstView, App, Backbone, Marionette, $, _) {
        FirstView.View = Marionette.ItemView.extend({
            //define view stuff in here
        });

        return FirstView.View;
    });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接