5

この 2 週間は、アプリケーションの作成だけでなく、バ​​ックボーンと関連ツールの学習にも費やしました。設計上の問題で壁にぶつかりました。利用可能なソリューションの種類と、バックボーンの専門家がこれを問題と見なしているかどうかを知りたいです。

問題: すべてのビューの依存関係を 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 によって生成されたイベントをどのようにサブスクライブするのかという問題に直面しました。イベントアグリゲーターにサブスクライブするには、誰かがそれを作成して初期化する必要がありますよね? その誰かがルーターである場合、すべてのビューをルーターで事前にインスタンス化する必要がありますか? それは意味がありません。

4

2 に答える 2

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呼び出しでは、すべてのスクリプトとテンプレートを事前にロードして評価する必要はありません。

問題の事実は、誰かがどこかでモジュールをインポートしなければならないということです。ルーターは、通常、ユーザーが特定のページ(表示)に移動したときに最初にヒットするコードであるため、適切な場所です。1つのルーターが原因であると思われる場合は、ルーターを複数のルーターに分割し、それぞれがアプリケーションの異なる「セクション」を担当するようにすることを検討してください。良い例えとして、典型的なMVCシナリオでのコントローラーについて考えてみてください。

複数のルーターの例

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は、すべてのPost関連のビュー(「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これにより、遅延ロードされたモジュールがビルドに含まれます。

編集:これは、RequireJSでのレイジーモジュールのロードと即時モジュールのロードを説明する要点です。

于 2013-01-06T21:08:13.657 に答える
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で使用します。私たちのルーターは、アプリの実際の状態とルートでイベントをトリガーするだけです。

于 2013-01-06T22:00:18.747 に答える