20

私は KnockoutJS が大好きですが、それを使って大規模な Javascript アプリケーションを構築する最善の方法を見つけるのに苦労しています。

現在、私がコードを処理している方法は、ルート ビュー モデルを使用して構築することです。これは通常、マスター ページ レベルで開始し、それを拡張します。私ko.applyBindings()はメインビューのみです。これが私が持っているサンプルコードです:

var companyNamespace = {};

// Master page. (a.k.a _Layout.cshtml)
(function(masterModule, $, ko, window, document, undefined) {
    var private = "test";

    masterModule.somePublicMethod = function() {};
    masterModule.viewModel = function() {
        this.stuff = ko.observable();
    };
}(companyNamespace.masterModule = companyNamespace.masterModule || {}, jQuery, ko, window, document));

// Index.cshtml.
(function(subModule, $, ko, window, document, undefined) {
    var private = "test";

    subModule.somePublicMethod = function() {};
    subModule.viewModel = function() {
        this.stuff = ko.observable();
    };

    $(document).ready(function() {
        ko.applyBindings(companyNamespace.masterModule);
    });
}(companyNamespace.masterModule.subModule = companyNamespace.masterModule.subModule || {}, jQuery, ko, window, document));

これはツリー構造であるため、ダブルマスターページなどを挿入する必要がある場合、リファクタリングが非常に面倒になるのではないかと心配しています。

考え?

編集

個別の要素にバインディングを適用してバインディングのスコープを変更できることは承知していますが、ビュー モデルがネストされている場合はどうなりますか?

4

2 に答える 2

44

私はかなり大きなノックアウト js 単一ページ アプリケーションを持っています。(現在 20,000 行以上のコード) これは、誰でも簡単に維持し、セクションを追加することができます。何百ものオブザーバブルがありますが、古い iPod touch のようなモバイル デバイスでも、パフォーマンスは依然として優れています。基本的には、一連のツールをホストするアプリケーションです。私が使用しているアプリケーションに関するいくつかの洞察を次に示します。

1. ビュー モデルは 1 つだけです。それは物事をシンプルに保ちます。

ビュー モデルは、各ページ (アプリ) の可視性、ナビゲーション、エラー、読み込み、トースト ダイアログなど、単一ページ アプリケーションの基本を処理します。これは、それがどのように見えるかの概要を説明するためのものです)

var vm = {

    error:
    {
        handle: function (error, status)
        {
           //Handle error for user here
        }
    },
    visibility:
    {
        set: function (page)
        {
            //sets visibility for given page
        }
    },
    permissions:
    {
        permission1: ko.observable(false),
        permission2: ko.observable(false)
        //if you had page specific permissions, you may consider this global permissions and have a separate permissions section under each app
    },
    loadDialog:
    {
        message: ko.observable(''),
        show: function (message)
        {
            //shows a loading dialog to user (set when page starts loading)
        },
        hide: function()
        {
            //hides the loading dialog from user (set when page finished loading)
        }
    },

    app1:
    {
        visible: ko.observable(false),
        load: function () 
        {
          //load html content, set visibility, app specific stuff here
        }
    },
    app2: 
    {
        visible: ko.observable(false),
        load: function () 
        {
          //load html content, set visibility, app specific stuff here
        }
    }

}

2. すべてのモデルは個別の .js ファイルに入ります。

私はモデルをクラスとして扱っているので、モデルが実際に行っているのは、変数を格納し、いくつかの基本的な書式設定関数を持っていることだけです (私はモデルをシンプルに保つようにしています)。モデル例:

    //Message Class
    function Message {
        var self = this;

        self.id = ko.observable(data.id);
        self.subject = ko.observable(data.subject);
        self.body = ko.observable(data.body);
        self.from = ko.observable(data.from);

    }

3. AJAX データベース呼び出しを独自の js ファイルに保持します。

セクションまたは「アプリ」で区切ることをお勧めします。たとえば、フォルダ ツリーが js/database/ で、app1.js と app2.js が、基本的な作成、取得、更新、および削除関数を含む js ファイルである場合があります。データベース呼び出しの例:

vm.getMessagesByUserId = function ()
{

    $.ajax({
        type: "POST",
        url: vm.serviceUrl + "GetMessagesByUserId", //Just a default WCF url
        data: {}, //userId is stored on server side, no need to pass in one as that could open up a security vulnerability
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        cache: false,
        success: function (data, success, xhr)
        {
            vm.messaging.sent.messagesLoaded(true);

            for (var i = 0; i < data.messages.length; i++)
            {
                var message = new Message({
                    id: data.messages[i].id,
                    subject: data.messages[i].subject,
                    from: data.messages[i].from,
                    body: data.messages[i].body
                });
                vm.messaging.sent.messages.push(message);
            }
        },
        error: function (jqXHR)
        {
            vm.error.handle(jqXHR.getResponseHeader("error"), jqXHR.status);
        }
    });
    return true;
};

4. すべてのモデル、ビュー モデル、およびデータベース js ファイルを 1 つにマージして縮小します。

「バンドルされた」js ファイルを作成できる Visual Studio の「Web Essentials」拡張機能を使用します。(js ファイルを選択し、それらを右クリックして Web Essentials --> Create Javascript Bundle File に移動します) 私のバンドル ファイルは次のように設定されています。

<?xml version="1.0" encoding="utf-8"?>
<bundle minify="true" runOnBuild="true">
  <!--The order of the <file> elements determines the order of them when bundled.-->

  <!-- Begin JS Bundling-->
  <file>js/header.js</file>


  <!-- Models -->

  <!-- App1 -->
  <file>js/models/app1/class1.js</file>
  <file>js/models/app1/class2.js</file>

  <!-- App2 -->
  <file>js/models/app2/class1.js</file>
  <file>js/models/app2/class2.js</file>

  <!-- View Models -->
  <file>js/viewModel.js</file>

  <!-- Database -->
  <file>js/database/app1.js</file>
  <file>js/database/app2.js</file>

  <!-- End JS Bundling -->
  <file>js/footer.js</file>

</bundle>

header.js と footer.js は、ドキュメント準備機能の単なるラッパーです。

header.js:

//put all views and view models in this
$(document).ready(function()
{

フッター.js:

//ends the jquery on document ready function
});

5. HTML コンテンツを分離します。

ナビゲートするのが難しい巨大な 1 つの巨大な html ファイルを保持しないでください。ノックアウトのバインドと HTTP プロトコルのステートレス性により、ノックアウトを使用すると、このトラップに簡単に陥る可能性があります。ただし、ユーザーが多くアクセスしていると見なすかどうかに応じて、分割に 2 つのオプションを使用します。

サーバー側のインクルード: (別の html ファイルへの単なるポインター。アプリのこの部分がユーザーによって頻繁に使用されていると思われる場合に使用しますが、それを別に保持したい場合)

<!-- Begin Messaging -->    
    <!--#include virtual="Content/messaging.html" -->
<!-- End Messaging -->

サーバー側のインクルードをあまり使用したくありません。そうしないと、ユーザーがページにアクセスするたびにロードする必要がある HTML の量がかなり大きくなります。そうは言っても、これは html を分離し、ノックアウト バインディングを維持するための最も簡単なソリューションです。

HTML コンテンツを非同期で読み込む: (アプリの特定の部分がユーザーによってあまり使用されない場合に使用します)

これを実現するために、jQuery ロード関数を使用します。

        // #messaging is a div that wraps all the html of the messaging section of the app
        $('#messaging').load('Content/messaging.html', function ()
        {
            ko.applyBindings(vm, $(this)[0]); //grabs any ko bindings from that html page and applies it to our current view model
        });

6. ページ/アプリの可視性を管理可能に保つ

ノックアウト.js アプリケーションのさまざまなセクションを表示したり隠したりすると、非常に多くの異なるオン/オフ スイッチを設定する必要があるため、管理と記憶が困難な大量のコード行が表示され、簡単に狂ってしまう可能性があります。まず、各ページまたはアプリを独自の "div" (および分離用の独自の html ファイル) に保持します。HTML の例:

<!-- Begin App 1 -->

<div data-bind="visible: app1.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>

<div data-bind="visible: app1.section1.visible()">
<!-- A branch off of app1 -->
</div>

<div data-bind="visible: app1.section2.visible()">
<!-- Another branch off of app1 -->
</div>

<!-- End App 1 -->


<!-- Begin App 2 -->
<div data-bind="visible: app2.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>
<!-- End App 2 -->

次に、サイトのすべてのコンテンツの可視性を設定する、これと同様の可視性関数を用意します (サブ関数でナビゲーションも処理します)。

vm.visibility:
{
    set: function (page)
    {
      vm.app1.visible(page === "app1");
      vm.app1.section1.visible(page === "app1section1");
      vm.app1.section2.visible(page === "app1section2");
      vm.app2.visible(page === "app2");     
    }
};

次に、アプリまたはページのロード関数を呼び出します。

<button data-bind="click: app1.load">Load App 1</button>

その中にこの機能があります:

vm.visibility.set("app1");

これで、大規模な単一ページ アプリケーションの基本がカバーされるはずです。私が提示したものよりも優れた解決策があるかもしれませんが、これは悪い方法ではありません。複数の開発者が、バージョン管理などと競合することなく、アプリケーションのさまざまなセクションで簡単に作業できます。

于 2013-03-12T16:46:23.897 に答える
7

プロトタイプの継承を使用してビュー モデルをセットアップするのが好きです。あなたのように、私は「マスター」ビューモデルを持っています。そのビュー モデルには、他のビュー モデルのインスタンスまたはビュー モデルの観察可能な配列が含まれており、そこからマークアップで「foreach」および「with」バインディングを使用できます。「foreach」および「with」バインディング内で、$data、$parent、$parents、および $root バインディング コンテキストを使用して、親ビュー モデルを参照できます。

KO ドキュメントの関連記事は次のとおりです。

foreach バインディング

バインディング付き

バインディング コンテキスト

必要に応じて、フィドルを一緒に投げることができます。お知らせ下さい。

于 2012-05-19T20:32:46.617 に答える