在我看来,与上述观点“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) {
var LibraryManager = new Marionette.Application();
LibraryManager.addRegions({
headerRegion: "#header-region",
mainRegion: "#main-region",
footerRegion: "footer-region",
dialogRegion: Marionette.Region.Dialog.extend({
el:"#dialog-region"
})
});
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 LibraryManager;
});
下一步,我们根据功能定义模块/子模块。这也将有一个特定于模块的路由器,并将连接控制器并处理路由。请注意对控制器的 require 调用。此代码位于
https://github.com/srihari-sridharan/LibraryManagement/blob/master/app/js/modules/books/booksModule.js。
define(['app'], function (LibraryManager) {
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.');
};
});
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 = {
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);
});
}
};
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({
});
View.Book = Marionette.ItemView.extend({
});
var NoBooksView = Marionette.ItemView.extend({
template: noneTemplate,
tagName: "tr",
className: "alert"
});
View.Books = Marionette.CompositeView.extend({
});
});
return LibraryManager.BooksModule.List.View;
});
控制器代码如下所示。这是从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) {
});
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) {
});
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) {
LibraryManager.dialogRegion.show(view);
});
});
booksListView.on("itemview:book:delete", function (childView, model) {
model.destroy();
});
LibraryManager.mainRegion.show(booksListLayout);
});
});
});
}
}
});
return LibraryManager.BooksModule.List.Controller;
});
因此,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({
});
return FirstView.View;
});
});