在路由器中管理视图实例化是否是一种好的做法



这是我的第一个骨干项目,我想知道我是否以最好的方式做事。我的应用程序基本上有两种状态,一种显示搜索框,另一种显示搜索框,其下有一个表格。我的路由器有用于搜索的路由和仅具有搜索视图的初始登录页面。当用户在查询中键入时,路由器将导航到搜索路由,并将表视图添加到页面中。这是我的路由器:

app.Router = Backbone.Router.extend({
    routes: {
        '': 'index',
        'search/coords=:address&age=:age&rad=:rad': 'search'
    },
    search: function(address, age, rad){    
        app.statusView || (app.statusView = new app.StatusView());
        app.searchView || (app.searchView = new app.SearchView());
        app.trigger('status:loading');
        app.Practices.fetch({
            reset: false,
            success: function() {
                app.searchView.setElement($('#search-box')).render();
                var searchQuery = new app.SearchQueryModel({age: age, coords: address.split(","), radius: rad});
                if (!app.tableView){
                    app.tableView = new app.TableView({model: searchQuery});
                } else {
                    app.tableView.model = searchQuery;
                    app.tableView.refresh();
                };
            }
        });
        app.trigger('status:clear');
    },
    index: function() {
        app.statusView = new app.StatusView();
        app.searchView = new app.SearchView();
        app.footerView = new app.FooterView();
        app.searchView.setElement($('#search-box')).render();
    }
});

如您所见,我的视图在索引路由中实例化,然后在搜索时使用相同的视图,除非用户直接转到搜索页面,在这种情况下,视图将在那里实例化。如果这不是很理想,我会感到惊讶,因为检查视图是否已经存在于搜索路线中似乎很笨拙。有没有更好的做事方法?

让我们说它还不错,但有一种更好的方法。

至于现在,您的路由器负责与应用程序状态的连接URL,以及视图和模型控制。第二个可能与路由器分离,因此您将需要控制器抽象,但 Backbone 不提供"开箱即用"的控制器。

但这不是问题,您可以使用插件或查看木偶中的控制器实现.js

这里的主要思想是在应用程序部分之间正确划分职责:

1( 路由器 - 通过控制器操作保留路由和连接 URL

2( 控制器 - 管理视图和模型(创建、删除、获取等(

3( 视图 - 侦听模型和 DOM 事件并渲染数据

4(模型 - 提供实际数据并使用数据。

首先欢迎来到 Backbone。 这是一个可爱的框架,可以让您随心所欲地制作美丽或丑陋的东西。 您的问题是关于视图实例化应该在哪里,就良好实践而言。 当然,在那里这样做似乎有点错误,因为它通过处理 url 路由和视图实例化违反了得墨忒耳定律

但是视图必须从某个地方运行,对吗? 如果不是路由器,那么在哪里?

所以我有两个回应:

  1. 如果你的应用程序很简单,你只是想玩骨干,那么你可能会没事的。 很多人让单页应用程序框架使原本简单的应用程序复杂化。 我不是想偷懒,但你现在拥有它的地方是 Backbone 中自然的初学者选择。 如果这是您的情况,请停在这里。

  2. 如果您想使用骨干网的全部功能来自定义制作框架,请继续阅读。

因此,我的设置旨在能够使用一些样板函数启动一个新项目,并且只创建几个特定于新应用程序的类。 路由处理和所有这些事情对我来说似乎足够低级,它应该只是我不想经常查看的某些配置的一部分。 结果是我的路由器看起来像这样:

define([
    'autorouter'
], function(AutoRouter){
    var AppRouter = AutoRouter.extend({
        autoRoutes: {
            ":page" : "routeDirect",
            ":page/:object" : "routeDirect",
            ":page/:object/:action" : "routeDirect",
            "": "routeDirect"
        }
    });
    return AppRouter;
});

然后对于每个新项目,我都有一个文件,用于保存非默认路由,例如:

define(function(require){
   return {
        "schedule" : require('screens/schedule')
      , "logout" : require('screens/logout')
      , "login" : require('screens/login')
      , "create" : require('screens/create')
      , "upload" : require('screens/upload')
      , "select" : require('screens/selection')
      , "inventory" : require('screens/inventory')
      , "describe" : require('screens/description')
   }
});

我将每个屏幕放入它自己的文件中(使用 requirejs 进行多文件依赖项管理(。 额外的变量被传递到screen

每个screen都是特定用户体验的大脑,负责加载视图,并可能在屏幕处于活动状态时处理某些事件。

如果这似乎是一个有趣的设置,那么这就是我是如何做到的:

对于路由器本身,我使用了一个样板类,我从Derick Bailey那里借来了一些小的修改:

define([
    'jquery', 'underscore', 'backbone'],
function($, _, Backbone) {
    var AutoRouter = Backbone.Router.extend({
      constructor: function(options){
          Backbone.Router.prototype.constructor.call(this, options);
          var that = this;
          that.app = options.app;
          if (this.autoRoutes){
            that.processAutoRoutes(options.app, that.autoRoutes);
          }
      },
      processAutoRoutes: function(app, autoRoutes){
          var method, methodName;
          var route, routesLength;
          var routes = [];
          var router = this;
          for(route in autoRoutes){
            routes.unshift([route, autoRoutes[route]]);
          }
          routesLength = routes.length;
          for (var i = 0; i < routesLength; i++){
            route = routes[i][0];
            methodName = routes[i][1];
            method = app[methodName];
            router.route(route, methodName, method);
          }
      }
    });
    return AutoRouter;
});

我永远不必看它,但我确实需要传递一个应用程序实例。 例如:

this.appRouter = new AppRouter({app : this});

最后我的路线方向函数:

define(function(require){
   var pathParser = function(path){
     return Array.prototype.slice.call(path);
   }
   var pathApply = function(path, routes, context){
     var pathArray = pathParser(path);
     var primary = pathArray[0];
     if (routes.hasOwnProperty(primary)){
         routes[primary].apply(context, pathArray.slice(1));
     } else {
         routes["default"].apply(context, pathArray.slice(1));
     }
   }
   return function(path){
     //NOTE PLEASE that this references AutoRouter
     //Which has an app property
     var oApp = this.app;
     var pathRoutes = _.extend(require('urls'), {
       "default" : require('screens/default')
     });
     pathApply(arguments, pathRoutes, oApp);
   };
});

那么,我让事情变得更好了吗? 好吧,如果你只用一两个屏幕做一些非常简单的事情,那么你肯定不想从头开始构建这种设置。 但是,如果你像我一样,并且希望能够快速生成新项目,那么像上面两个类这样的样板允许一个 JSON 对象告诉应用程序我应该将哪些路由发送到哪些屏幕。 然后,我可以将所有逻辑放在适当的位置,从而允许分离关注点。 这就是为什么我认为Backbone如此令人愉快。

我对你的问题的理解是,每次点击搜索时都会触发一条路线。

如果这是您执行此操作的方式,请使用视图事件哈希(用于捕获和处理视图中发生的事件(进行搜索。不要使用路由。在视图中定义事件哈希,并具有回调来处理搜索。

var myAppEventBus = _.extend({},Backbone.Events);
var myAppController = {
  function : search(options) {
    // create an instance of the collection and do a fetch call passing the 
    // search parameters to it.
  
    var searchResultsCollection = new SearchResultsCollection();
  
    // pass search criteria, the success and error callbacks to the fetch
    // method.
    var that = this;
    searchResultsCollection.fetch(
      {
        data:that.options,
        success : function() {
            // Pass the fetched collection object in the trigger call so that
            // it can be
            // received at the event handler call back
            var options = {
              "searchResultsCollection" : that.searchResultsCollection;
            };
            myAppEventBus.trigger("search_event_triggered",options);
     
        },
        error : function() {
          // do the error handling here.
        }
      }
    );
    
     }
};
// Application Router.
var MyAppRouter = Backbone.Router.extend({
  routes : {
    'search/coords=:address&age=:age&rad=:rad': 'search'
  },
  
  search : function(searchParams) {
    // Fetch the query parameters and pass it to the view.
    
    var routeSearchExists = false;
    var searchOptions = {};
    var options = {};
    
    if(searchParams) {
      routeSearchExists = true;
      // If search params exist split and set them accordingly in
      // the searchOptions object.
      
      options.searchOptions = searchOptions;
    }
    
    // Create and render the search view. Pass the searchOptions
    var searchView = new SearchView(options);
    searchView.render();
    
    // Create and render an instance of the search results view.
    var searchResultsView = new SearchResultsView();
    searchResultsView.render();
    
    // If there are search parameters from the route, then do a search.
    if(routeSearchExists) {
      searchView.search();
    }
  }
  
});
// The main view that contains the search component and a container(eg: div) 
// for the search results.
var SearchView = Backbone.View.extend({
  el : "#root_container",
  
  searchOptions : null,
  
  initialize : function(options) {
    // Intialize data required for rendering the view here.
    // When the user searches for data thru routes, it comes down in the 
    // options hash which can then be passed on to the controller.
    if(options.searchOptions) {
      this.searchOptions = options.searchOptions;    
    }
  },
  
  events : {
    "search #search_lnk":"initSearch"
  },
  
  initSearch : function(event) {
    event.preventDefault();
    var searchOptions = {};
    // Fetch the search fields from the form and build the search options.
    myAppController.search(searchOptions);
    
  },
  
  search : function() {
    if(this.searchOptions) {
       myAppController.search(searchOptions);
    }
  }
  
});
// The view to display the search results.
var SearchResultsView = Backbone.View.extend({
  
  
  searchResultsCollection : null;
  
  initialize : function(options) {
    
    // Handling the triggered search event.
    myAppEventBus.on("search_event_triggered",this.render,this);
  },
  
  
  render : function(options) {
    //search results collection is passed as a property in options object.
    if(options.searchResultsCollection)
       //Render your view.
    else
      // Do it the default way of rendering.
      
  }
});

  1. SearchView 是包含搜索组件和用于保存搜索结果的容器(如div(的根视图。
  2. 搜索结果视图显示搜索结果。
  3. 单击搜索选项时,事件回调 (initSearch( 将获取输入的搜索数据。
  4. 调用 myAppController 对象上的搜索方法并传递搜索查询。
  5. 创建搜索集合的实例并调用 fetch,向其传递搜索查询以及成功和错误回调。
  6. 成功后,将触发自定义主干事件以及提取的集合。
  7. 调用此事件的回调(搜索结果视图中的呈现方法(。
  8. 回调呈现搜索结果。
  9. 在路由器中加载时,可以创建两个视图的实例(结果视图将为空(并将其附加到 dom。
  10. 如果您希望在 url 上按多个查询字符串进行搜索,那么我建议您使用以下路由。搜索?*查询字符串。
  11. 在路由回调中,调用实用程序函数,拆分查询字符串并返回搜索对象并将搜索字符串传递给视图。

相关内容

最新更新