我花了两周时间学习骨干和相关工具以及编写应用程序。我遇到了设计问题,想知道可用的解决方案类型,以及Backbone专家是否认为这是一个问题。
问题:我最终不得不将所有视图依赖项放在我的router.js中,并且我无法弄清楚它们是否可以解决这个问题。以下是我的router.js代码:
// router.js
define([
'jquery',
'underscore',
'backbone',
'text',
'views/landing',
'views/dashboard',
],
function($, _, Backbone, t,LandingView,DashboardView){
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'': 'showLanding',
'projects': 'showProjects',
// Default
'*actions': 'defaultAction'
},
navigate_to: function(model){
alert("navigate_to");
},
showProjects: function() {},
showLanding: function() {},
});
var initialize = function() {
var app_router = new AppRouter;
Backbone.View.prototype.event_aggregator = _.extend({}, Backbone.Events);
// Extend the View class to include a navigation method goTo
Backbone.View.prototype.goTo = function (loc) {
app_router.navigate(loc, true);
};
app_router.on('route:showLanding', function(){
var landing = new LandingView();
});
app_router.on('route:showProjects', function(){
var dashboard=new DashboardView();
});
app_router.on('defaultAction', function(actions){
alert("No routes");
// We have no matching route, lets just log what the URL was
console.log('No route:', actions);
});
Backbone.history.start({pushState: true});
};
return {
initialize: initialize
};
});
router.js包含 LandingView 和 DashboardView 视图,这些视图又会获取相应的模板。初始路由加载具有登录模板的LandingView。登录后,它调用router.js的goTo方法来生成DashboardView()。虽然这有效,但我觉得它有点难看。但我无法想象如何从LandingView中生成新的DashboardView,而无需直接从LandingView()内部或从路由器引用DashboardView()。
如果我继续通过router.js这样做,我将最终直接或间接地从路由器中提取我的所有视图js文件。听起来有点难看!
我查看了Derick Baileys的事件聚合器模式但面临的问题是,如果DashboardView的实例甚至不存在,DashboardView如何订阅由LandingView生成的事件?有人必须创建并初始化它才能订阅事件聚合器,对吧?如果有人是路由器,我是否需要在路由器中预先实例化所有视图?这没有意义。
答案 0 :(得分:11)
我通过首次点击路线时导入视图解决了这个问题:
define(['backbone'], function(Backbone) {
var AppRouter = Backbone.Router.extend({
routes: {
'': 'home',
'users': 'users'
},
home: function() {
requirejs(["views/home/mainview"], function(HomeView) {
//..initialize and render view
});
},
users: function() {
requirejs(["views/users/mainview"], function(UsersView) {
//..initialize and render view
});
}
});
return AppRouter;
});
它无法解决必须最终将所有视图导入路由器的问题,但是懒惰的requirejs
调用不强制加载和评估所有脚本和模板前面。
事实是某个人必须在某处导入模块。路由器是一个明智的位置,因为它通常是用户导航到某个页面时查看的第一段代码(View)。如果您觉得某个路由器负责太多,您应该考虑将路由器拆分为多个路由器,每个路由器负责应用程序的不同“部分”。在典型的MVC场景中,想象一下Controller是一个很好的类比。
userrouter.js 处理所有与用户相关的观看次数('users /'下的路线):
define(['backbone'], function(Backbone) {
var UserRouter = Backbone.Router.extend({
routes: {
'users', 'allUsers',
'users/:id', 'userById'
},
allUsers: function() {
requirejs(["views/users/listview"], function(UserListView) {
//..initialize and render view
});
},
userById: function(id) {
requirejs(["views/users/detailview"], function(UserDetailView) {
//..initialize and render view
});
}
});
return UserRouter;
});
postrouter.js 处理所有与帖子相关的观点('posts /'下的路线):
define(['backbone'], function(Backbone) {
var PostRouter = Backbone.Router.extend({
routes: {
'posts', 'allPosts',
'posts/:id', 'postById'
},
allPosts: function() {
requirejs(["views/posts/listview"], function(PostListView) {
//..initialize and render view
});
},
postById: function(id) {
requirejs(["views/posts/detailview"], function(PostDetailView) {
//..initialize and render view
});
}
});
return PostRouter;
});
approuter.js 是主路由器,它在应用程序启动时启动并初始化所有其他路由。
define(['backbone', 'routers/userrouter', 'routers/postrouter'],
function(Backbone, UserRouter, PostRouter) {
var AppRouter = Backbone.Router.extend({
routes: {
'', 'home',
},
initialize: function() {
//create all other routers
this._subRouters = {
'users' : new UserRouter(),
'posts' : new PostRouter()
};
},
start: function() {
Backbone.history.start();
},
home: function() {
requirejs(["views/home/mainview"], function(HomeView) {
//..initialize and render view
});
}
});
return UserRouter;
});
最后,你的应用程序的 main.js ,启动应用程序路由器:
new AppRouter().start();
通过这种方式,您可以保持每台路由器的精简,并避免在实际需要之前解析依赖树。
旁注:如果您使用嵌套的requirejs
调用并且正在使用r.js
进行构建,请记住设置构建选项findNestedDependencies:true
,以便延迟加载的模块包含在构建中。
修改:这是gist that explains lazy vs. immediate module loading in RequireJS。
答案 1 :(得分:1)
我们使用工厂为它只返回一个视图实例,它也可以缓存实例:
define(function() {
// Classes are defined like this { key1: Class1, key2: Class2 }
// not cachedObjects are defined like this { notCached : { key3: Class3 }}
return function(Classes) {
var objectCache = {};
return {
get: function(key, options) {
var cachedObject = objectCache[key];
if (cachedObject){
return cachedObject;
}
var Class = Classes[key];
if (Class) {
cachedObject = new Class(options);
objectCache[key] = cachedObject;
return cachedObject;
}
Class = Classes.notCached[key];
if (Class) {
return new Class(options);
}
}
};
};
});
然后我们有一个创建工厂的模块:
define([
'common/factory',
'views/view1',
'views/view2',
'views/view3',
], function(
viewCache,
View1,
View2,
View3
) {
var views = {
route1: View1,
route2: View2,
notCached: {
route3: View3,
}
};
return viewCache(views);
});
在路由器中,您可以通过调用viewCache.get(route)轻松获取视图。好处是可以分离视图的创建/缓存,现在可以单独测试。
同样,当我们使用Marionette时,我们不使用路由器中的viewCache,而是使用RegionManager,它更适合创建视图。我们的路由器只是触发应用程序的实际状态和路径的事件。