使用Backbone.js路由器浏览通过require.js模块化的视图
我需要将我的视图和路由器分离成不同的文件。然后我有一个main.js文件来实例化路由器,并呈现我的默认视图 我的路由器有view('view/:id')和edit('edit/:id')作为路由。在main.js中,当我实例化路由器时,我可以硬编码router.navigate('View/1',true),导航工作正常。在我的视图文件中,当我单击编辑链接时,我想调用router.navigate('view/'+id,true),但我不确定该如何操作 我成功地调用了Backbone.history.navigate('View/'+id,true),但我觉得我不应该依赖全局主干对象 我尝试将({router:appRouter})传递给我的视图,这样我就可以使用这个.options.router.navigate(),但是这对我不起作用 如果您感到好奇,以下是我的应用程序中的一组代码: 路由器:使用Backbone.js路由器浏览通过require.js模块化的视图,backbone.js,requirejs,Backbone.js,Requirejs,我需要将我的视图和路由器分离成不同的文件。然后我有一个main.js文件来实例化路由器,并呈现我的默认视图 我的路由器有view('view/:id')和edit('edit/:id')作为路由。在main.js中,当我实例化路由器时,我可以硬编码router.navigate('View/1',true),导航工作正常。在我的视图文件中,当我单击编辑链接时,我想调用router.navigate('view/'+id,true),但我不确定该如何操作 我成功地调用了Backbone.histo
define(['./View', './Edit'], function (View, Edit) {
return Backbone.Router.extend({
routes: {
'View/:id': 'view',
'Edit/:id': 'edit'
},
view: function (id) {
var model = this.collection.get(id);
var view = new View({ model: model });
view.render();
},
edit: function (id) {
var model = this.collection.get(id);
var edit = new Edit({ model: model });
edit.render();
}
});
});
视图:
您可以使用window.location.hash:)使用老式的方法完成此操作 如果不需要显式路由,这里有一个替代解决方案。当应用程序启动时,创建一个扩展主干事件的对象
window.EventDispatcher = _.extend({}, Backbone.Events);
然后,您可以在应用程序中的任何位置收听事件
EventDispatcher.bind("mymodel:edit", this.editHandler, this);
此外,还可以从任何地方发送事件,数据
下面是您希望随车发送的任何参数
EventDispatcher.trigger("mymodel:edit", data);
与几乎所有的主干问题一样,有很多方法可以解决这个问题。我在当前项目中采用的方法是将所有内容放在全局自定义名称空间中,并使用该名称空间传递必要的引用:
var MyNamespace = {};
MyNamespace.init = function() {
MyNamespace.appView = new MyAppView();
MyNamespace.router = new MyRouter();
// etc
}
视图可以根据需要引用MyNamespace.router
。但是require.js似乎不支持这样做,所以这里有一些其他选项:
- 永远不要显式地调用路由器-而是更改路由器侦听的全局状态对象。这实际上就是我在当前项目中所做的事情——请参阅以了解更多详细信息
- 将路由器连接到顶级视图(通常称为
),使其全局可访问,然后使用AppView
AppView.router.navigate()
- 制作另一个模块,该模块提供一个
实用程序函数,该函数在内部调用导航
。这与您正在做的并没有太大的不同,但它会使它稍微模块化一些,并防止您一直使用全局引用。这还允许您更改内部实现主干.history.navigate()
router.js
中将有一个initialize()
函数。我修改了我的initialize()
函数,如下所示:
initialize = function () {
var app_router;
app_router = new AppRouter();
// Extend the View class to include a navigation method goTo
Backbone.View.goTo = function (loc) {
app_router.navigate(loc, true);
};
Backbone.history.start();
};
由于backbone.js特殊的继承风格,这允许我调用
MyView.goTo(location)代码>在我的任何视图中。对我来说,带有goTo函数的解决方案只做了一点小小的更改
Backbone.View.prototype.goTo = function (loc) {
appRouter.navigate(loc, true);
};
我知道这个问题由来已久,但我想知道为什么您没有使用require.js来获取路由器:
define(['./PathToRouter', ], function (router) {
return Backbone.View.extend({
template: Handlebars.compile($('#template').html()),
events: {
'click .edit': 'edit'
},
render: function () {
//Create and insert the cover letter view
$(this.el).html(this.template(this.model.toJSON()));
$('#View').html(this.el);
return this;
},
edit: function () {
router.navigate('Edit/' + this.model.id, true);
}
});
});
这种方法怎么样?由于主干网在其所有4个组件中都实现了模板模式,只要稍加设计,您就可以通过应用程序的路由器为每个视图提供一个简单的导航机制,而无需进行任何循环引用(这是我在其他类似文章中看到的,但请尽量避免)
路由器组件,与其他路由器示例没有太大区别:
define('Router', ['backbone', ... ],
function (Backbone, ...) {
var MyRouter = Backbone.Router.extend({
routes: {
'viewA': 'viewA',
'viewB': 'viewB'
},
initialize: function () {
...
};
},
viewA: function () {
...
},
viewB: function () {
...
}
});
return MyRouter;
});
应用程序,创建路由器实例并激发通过此实例的第一个视图:
define('App', ['backbone', ...
], function (Backbone, ...) {
function initialize() {
//route creation
if (!this.routes)
routes = new Router(this);
//backbone history start
Backbone.history.start();
//ViewA navigation, bigbang
if (!this.viewA)
this.viewA = new ViewA({router: this.routes});
this.viewA.render();
}
return {
initialize: initialize
};
});
BaseView、所有视图的基本构造函数定义以及导航方法:
define('BaseView', ['jquery', 'underscore', 'backbone', ...
], function ($, _, Backbone, ...) {
var BaseView;
BaseView = Backbone.View.extend({
id: '...',
constructor: function (options) {
this.router = options.router;
this.model = options.model;
Backbone.View.prototype.constructor.call(this);
},
initialize: function () {
this.template = _.template(tpl);
},
events: {
},
render: function () {
$(this.el).html(this.template());
return this;
},
//Provides transparent navigation between views throught the backbonejs
//route mechanism
navigate: function(pageId)
{
this.router.navigate(pageId, {trigger: true});
}
});
return BaseView;
});
作为视图实例,每个视图从基本视图而不是主干视图扩展,并继承基本行为:
define('ViewA', ['jquery', 'underscore', 'backbone', 'BaseView'
], function ($, _, Backbone, BaseView) {
var ViewA;
ViewA = BaseView.extend({
id: '...',
constructor: function (options) {
this._super("constructor");
},
...
foo: function()
{
...
this.navigate("viewB");
}
});
return ViewA;
});
它适合我,也可以在其他项目中重用。对于我,我向主应用程序添加了一个对象,如下所示
define(['jquery','underscore','backbone','views/personView','views/peopleView','views/textView'],function($,_,backbone,PersonView,PeopleView,TitleView){
var Router = Backbone.Router.extend({
routes:{
'':'home',
'edit/:id':'editPerson',
'new':'editPerson',
'delete/:id':'deletePerson'
}
})
var initialize = function(){
var router = new Router();
window.app = {
router:router
}
router.on('route:home',function(){
})
//enable routing using '#'[hashtag] navigation
Backbone.history.start();
};
return {
initialize:initialize
};
});
在视图中,可以说是windows.app.router.navigate({“”,trigger:true})。我不知道在这种情况下,全局作用域是否是一种好的做法,但它对我很有效。我有一个新的AMD模块布线解决方案
RequireJS路由器
这采用了在导航到每个页面时延迟加载AMD模块的方法。对于主干路由器,您需要预先将所有视图作为依赖项。这将在加载第一页时加载所有应用程序Javascript。当您导航到每个路由时,RequireJS路由器延迟加载模块
用于运行应用程序的示例main.js
define([], function() {
'use strict';
// Configure require.js paths and shims
require.config({
paths: {
'text': 'bower_components/requirejs-text/text',
'router': 'bower_components/requirejs-router/router'
}
});
// Load the router and your layout
require(['router', 'js/layout/layoutView'], function(router, LayoutView) {
var layoutView = new LayoutView();
// The layout's render method should draw the header, footer, and an empty main-content section
// then load the content section.
// render: function() {
// this.$el.html(this.template({model: this.model}));
// router.loadCurrentRoute();
// }
// Configure the router
router
.registerRoutes({
home: {path: '/', moduleId: 'home/homeView'},
order: {path: '/order', moduleId: 'order/orderView'},
notFound: {path: '*', moduleId: 'notFound/notFoundView'}
})
.on('statechange', function() {
// Render the layout before loading the current route's module
layoutView.render.call(layoutView);
})
.on('routeload', function(module, routeArguments) {
// Attach the content view to the layoutView's main-content section
layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el);
})
.init({
// We're manually calling loadCurrentRoute() from layoutView.render()
loadCurrentRouteOnStateChange: false
});
);
);
在发现可以使用全局主干对象之前,我尝试使用window.location,但它实际上没有触发我的事件,这可能是一个完全不同的问题。如果您仅使用路由触发事件(您不需要url进行明显更改,也不需要用户使用后退/前进按钮),那么今天下午我想出来的东西可能会有所帮助。您可以制作一个全局“EventDispatcher”,我在上面的回答中添加了它。谢谢KreeK。我一直希望URL对这个特定的应用程序很重要。我见过至少一个类似于全局事件调度器的示例。谢谢您的帮助。您是否在require模块之外声明MyNamespace?这接近于使用全局主干对象,只是使用我自己的对象,对吗?我没有使用require.js,所以它不适用于我的情况。我想我列表中的最后一个项目可能是最接近require.js的习惯用法。是的,我自己的工具调用navigate并不是一个坏主意。如果我这样做的话,我可能会让同样的实用程序将我的路由器作为一个选项显示出来,也许这会解决整个问题。嗨,伙计,我正在做类似于你的事情,打算在我的视图模块中使用router.navigate()。你最终是如何做到这一点的?Thx非常提前。我最终使用Backgone.history.navigate。因为我把我的观点分成了两部分
define(['jquery','underscore','backbone','views/personView','views/peopleView','views/textView'],function($,_,backbone,PersonView,PeopleView,TitleView){
var Router = Backbone.Router.extend({
routes:{
'':'home',
'edit/:id':'editPerson',
'new':'editPerson',
'delete/:id':'deletePerson'
}
})
var initialize = function(){
var router = new Router();
window.app = {
router:router
}
router.on('route:home',function(){
})
//enable routing using '#'[hashtag] navigation
Backbone.history.start();
};
return {
initialize:initialize
};
});
define([], function() {
'use strict';
// Configure require.js paths and shims
require.config({
paths: {
'text': 'bower_components/requirejs-text/text',
'router': 'bower_components/requirejs-router/router'
}
});
// Load the router and your layout
require(['router', 'js/layout/layoutView'], function(router, LayoutView) {
var layoutView = new LayoutView();
// The layout's render method should draw the header, footer, and an empty main-content section
// then load the content section.
// render: function() {
// this.$el.html(this.template({model: this.model}));
// router.loadCurrentRoute();
// }
// Configure the router
router
.registerRoutes({
home: {path: '/', moduleId: 'home/homeView'},
order: {path: '/order', moduleId: 'order/orderView'},
notFound: {path: '*', moduleId: 'notFound/notFoundView'}
})
.on('statechange', function() {
// Render the layout before loading the current route's module
layoutView.render.call(layoutView);
})
.on('routeload', function(module, routeArguments) {
// Attach the content view to the layoutView's main-content section
layoutView.$('#main-content').replaceWith(new module(routeArguments).render().el);
})
.init({
// We're manually calling loadCurrentRoute() from layoutView.render()
loadCurrentRouteOnStateChange: false
});
);
);