使用Backbone.js路由器浏览通过require.js模块化的视图

我将我的意见和路由器分开与要求单独的文件。 然后我有一个main.js文件实例化路由器,并呈现我的默认视图。

我的路由器有视图('查看/:id')和编辑('编辑/:id')作为路由。 在main.js中,当我实例化路由器时,我可以硬编码router.navigate('View / 1',true),导航工作正常。 在我的视图文件中,当我点击编辑链接时,我想调用router.navigate('View /'+ id,true),但我不知道我应该怎么做。

我已经成功地调用Backbone.history.navigate('View /'+ id,true),但我不觉得我应该依赖全局Backbone对象。

我尝试通过({路由器:appRouter})到我的意见,所以我可以使用this.options.router.navigate(),但是这不是为我工作。

如果你好奇,下面是我的应用程序的一堆代码:

路由器:

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

视图:

 define(function () { 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 () { Backbone.history.navigate('Edit/' + this.model.id, true); }, }); }); 

就像几乎所有的Backbone问题一样,有很多方法可以解决这个问题。 我在当前项目中采用的方法是将所有内容放在全局自定义名称空间中,并使用它来传递必要的引用:

 var MyNamespace = {}; MyNamespace.init = function() { MyNamespace.appView = new MyAppView(); MyNamespace.router = new MyRouter(); // etc } 

然后视图可以根据需要引用MyNamespace.router 。 但看起来这不会/不鼓励与require.js,所以这里有一些其他的select:

  • 不要明确地调用路由器 – 而是更改路由器监听的全局状态对象。 这实际上是我在当前项目中完成的工作 – 请参阅此答复以获取更多详细信息。

  • 将路由器连接到通常称为AppView顶层视图,使其可以全局访问,并使用AppView.router.navigate()

  • 制作另一个提供navigate实用程序function的模块,在内部调用Backbone.history.navigate() 。 这和你正在做的事情没什么太大的区别,但它会使它稍微更加模块化,并且不让你始终使用全局引用。 这也可以让你改变内部的实现。

如果其他人正在寻找像我这样的问题的解决scheme,我发布了我最终做的事情。 如果使用样板backbone.js,那么你将在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的特殊的inheritance风格,这允许我调用MyView.goTo(location); 从我的任何意见。

你可以用window.location.hash这个老式的方法来做到这一点:)

 window.location.hash = "Edit/1" 

如果你不需要显式的路由,这是一个备用的解决scheme。 当你的应用程序启动时,创build一个扩展骨干事件的对象

 window.EventDispatcher = _.extend({}, Backbone.Events); 

然后在你的应用程序的任何地方,你可以听取事件

 EventDispatcher.bind("mymodel:edit", this.editHandler, this); 

而且从任何地方发送事件,下面的data是你想要发送的任何参数

 EventDispatcher.trigger("mymodel:edit", data); 

对我来说,goTo函数的解决scheme稍有改动

  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个组件中实现了模板模式,通过一些devise,您可以通过应用程序的路由器为每个视图提供一个简单的导航机制,而无需进行任何循环引用(这是我在其他类似post中看到的,但尽量避免它)。

路由器组件,与其他路由器的例子没有太大的区别:

 define('Router', ['backbone', ... ], function (Backbone, ...) { var MyRouter = Backbone.Router.extend({ routes: { 'viewA': 'viewA', 'viewB': 'viewB' }, initialize: function () { ... }; }, viewA: function () { ... }, viewB: function () { ... } }); return MyRouter; }); 

应用程序,创build路由器实例并激发通过此实例的第一个视图:

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

一个View实例,每个视图都从基础实体延伸而不是主干,并inheritance基础行为:

 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模块的解决scheme。

RequireJS路由器https://github.com/erikringsmuth/requirejs-router

当您导航到每个页面时,这需要延迟加载AMD模块的方法。 使用Backbone路由器,您需要预先将所有视图作为依赖项。 这加载了所有的应用程序的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 }); ); );