MVVM與Backbone demo

MVVM

https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodelcss

Model–view–view-model (MVVM) is a software architectural pattern.html

MVVM facilitates a separation of development of the graphical user interface – be it via a markup language or GUI code – from development of the business logic or back-end logic (the data model). The view model of MVVM is a value converter;[1] meaning the view model is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented. In this respect, the view model is more model than view, and handles most if not all of the view's display logic.[1] The view model may implement a mediator pattern, organizing access to the back-end logic around the set of use cases supported by the view.jquery

 

View model
The view model is an abstraction of the view exposing public properties and commands. Instead of the controller of the MVC pattern, or the presenter of the MVP pattern, MVVM has a binder. In the view model, the binder mediates communication between the view and the data binder. [clarification needed] The view model has been described as a state of the data in the model. [7]
Binder
Declarative data- and command-binding are implicit in the MVVM pattern. In the Microsoft solution stack, the binder is a markup language called XAML. [8] The binder frees the developer from being obliged to write boiler-plate logic to synchronize the view model and view. When implemented outside of the Microsoft stack the presence of a declarative databinding technology is a key enabler of the pattern. [4] [9] [clarification needed]

 

國內權威解釋:

https://linux.cn/article-6481-1.html?utm_medium=weibolinux

MVVM表明的是Model-View-ViewModel,這裏須要解釋一下什麼是ViewModel。ViewModel的含義就是 "Model of View",視圖的模型。它的含義包含了領域模型(Domain Model)和視圖的狀態(State)。 在圖形界面應用程序當中,界面所提供的信息可能不只僅包含應用程序的領域模型。還可能包含一些領域模型不包含的視圖狀態,例如電子表格程序上須要顯示當前排序的狀態是順序的仍是逆序的,而這是Domain Model所不包含的,但也是須要顯示的信息。git

能夠簡單把ViewModel理解爲頁面上所顯示內容的數據抽象,和Domain Model不同,ViewModel更適合用來描述View。程序員

mvvm-call

MVVM的調用關係和MVP同樣。可是,在ViewModel當中會有一個叫Binder,或者是Data-binding engine的東西。之前所有由Presenter負責的View和Model之間數據同步操做交由給Binder處理。你只須要在View的模版語法當中,指令式地聲明View上的顯示的內容是和Model的哪一塊數據綁定的。當ViewModel對進行Model更新的時候,Binder會自動把數據更新到View上去,當用戶對View進行操做(例如表單輸入),Binder也會自動把數據更新到Model上去。這種方式稱爲:Two-way data-binding,雙向數據綁定。能夠簡單而不恰當地理解爲一個模版引擎,可是會根據數據變動實時渲染。github

 

MVC 和 MVP

https://linux.cn/article-6481-1.html?utm_medium=weiboweb

用戶的對View操做之後,View捕獲到這個操做,會把處理的權利交移給Controller(Pass calls);Controller會對來自View數據進行預處理、決定調用哪一個Model的接口;而後由Model執行相關的業務邏輯;當Model變動了之後,會經過觀察者模式(Observer Pattern)通知View;View經過觀察者模式收到Model變動的消息之後,會向Model請求最新的數據,而後從新更新界面。以下圖:docker

mvc-call

 

MVC模式同樣,用戶對View的操做都會從View交移給Presenter。Presenter會執行相應的應用程序邏輯,而且對Model進行相應的操做;而這時候Model執行完業務邏輯之後,也是經過觀察者模式把本身變動的消息傳遞出去,可是是傳給Presenter而不是View。Presenter獲取到Model變動的消息之後,經過View提供的接口更新界面數據庫

關鍵點:

  1. View再也不負責同步的邏輯,而是由Presenter負責。Presenter中既有應用程序邏輯也有同步邏輯。
  2. View須要提供操做界面的接口給Presenter進行調用。(關鍵)

mvp-call

 

Segment解釋 ViewModel

https://segmentfault.com/q/1010000000534091

而MVVM派的見解是,我給view裏面的各類控件也定義一個對應的數據對象,這樣,只要修改這個數據對象,view裏面顯示的內容就自動跟着刷新,而在view 裏作了任何操做,這個數據對象也跟着自動更新,這樣多美。因此:

ViewModel:就是與界面(view)對應的Model。由於,數據庫結構每每是不能直接跟界面控件一一對應上的,因此,須要再定義一個數據對象專門對應view上的控件。而ViewModel的職責就是把model對象封裝成能夠顯示和接受輸入的界面數據對象。

至於viewmodel的數據隨着view自動刷新,而且同步到model裏去,這部分代碼能夠寫成公用的框架,不用程序員本身操心了。

 

想好說清楚,最好就是使用用例來解釋MVVM是如何工做的,下圖以AngularJS爲例。

如圖所示,App.html是與app.js中的ViewModel綁定的View。若是有人想要修改app.js中的$scope.username屬性,那麼它會自動更新app.html中對應的name屬性。反之也成立,若是有人想要修改app.html中的name屬性值,那麼也會自動更新$scope.user對象。App.html同時也能夠對app.js發出命令,如調用$scope.save函數從而讓app.js執行一些邏輯操做,如保存$scope.userusers.js模型中並與後端同步,那麼它就能夠與整個應用程序共享。

MVVM模式對於RIA應用來講是很是必要的,由於View被綁定至ViewModel的,而且當ViewModel的狀態變化時,View會自動更新,它有效的隔離了View和它背後的業務邏輯。這也是爲何AngularJS須要雙向綁定的緣由。

 

 

知乎解釋

https://www.zhihu.com/question/28893576

去年暑假實習一直在作MVVM的開發。就我所知全部的後端和業務邏輯都應該是在M端的,寫界面XAML是V端,先後端之間能夠互不干擾完成設計/開發,最後再加一個VM,而VM作的事就是銜接V和M。 Wiki上這一段很清楚地解釋了view model的做用,也就是把model中的數據對象轉換成view能夠直接使用的形式(好比string或者collection等等)。

 

視圖層 V,展示數據,綁定事件,

視圖模型層 VM, 從數據模型層變換而來, 除了數據或狀態flag,還有許多事件回調,$watch回調(當某個數據變更時會執行的函數), widget的配置對象

模型層 M, 這是經過AJAX請求獲得,或者經過後端輸出時就打印在頁面某個script標籤內, 都是數據(JSON),沒有函數

業務邏輯層 BS,這不歸爲MVVM以內,但它們應該使用類進行組織,在VM中的某個函數裏面被調用。

 

Backbone

http://backbonejs.org/#Getting-started

Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.

 

When working on a web application that involves a lot of JavaScript, one of the first things you learn is to stop tying your data to the DOM. It's all too easy to create JavaScript applications that end up as tangled piles of jQuery selectors and callbacks, all trying frantically to keep data in sync between the HTML UI, your JavaScript logic, and the database on your server. For rich client-side applications, a more structured approach is often helpful.

 

With Backbone, you represent your data as Models, which can be created, validated, destroyed, and saved to the server. Whenever a UI action causes an attribute of a model to change, the model triggers a "change" event; all the Views that display the model's state can be notified of the change, so that they are able to respond accordingly, re-rendering themselves with the new information. In a finished Backbone app, you don't have to write the glue code that looks into the DOM to find an element with a specific id, and update the HTML manually — when the model changes, the views simply update themselves.

 

Philosophically, Backbone is an attempt to discover the minimal set of data-structuring (models and collections) and user interface (views and URLs) primitives that are generally useful when building web applications with JavaScript. In an ecosystem where overarching, decides-everything-for-you frameworks are commonplace, and many libraries require your site to be reorganized to suit their look, feel, and default behavior — Backbone should continue to be a tool that gives you the freedom to design the full experience of your web application.

Backbone Demo

todolist

http://backbonejs.org/examples/todos/index.html

 

 

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Backbone.js Todos</title>
  <link rel="stylesheet" href="todos.css"/>
</head>

<body>

  <div id="todoapp">

    <header>
      <h1>Todos</h1>
      <input id="new-todo" type="text" placeholder="What needs to be done?">
    </header>

    <section id="main">
      <input id="toggle-all" type="checkbox">
      <label for="toggle-all">Mark all as complete</label>
      <ul id="todo-list"></ul>
    </section>

    <footer>
      <a id="clear-completed">Clear completed</a>
      <div id="todo-count"></div>
    </footer>

  </div>

  <div id="instructions">
    Double-click to edit a todo.
  </div>

  <div id="credits">
    Created by
    <br />
    <a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>.
    <br />Rewritten by: <a href="https://github.com/tastejs/todomvc">TodoMVC</a>.
  </div>

  <script src="../../test/vendor/json2.js"></script>
  <script src="../../test/vendor/jquery.js"></script>
  <script src="../../test/vendor/underscore.js"></script>
  <script src="../../backbone.js"></script>
  <script src="../backbone.localStorage.js"></script>
  <script src="todos.js"></script>

  <!-- Templates -->

  <script type="text/template" id="item-template">
    <div class="view">
      <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
      <label><%- title %></label>
      <a class="destroy"></a>
    </div>
    <input class="edit" type="text" value="<%- title %>" />
  </script>

  <script type="text/template" id="stats-template">
    <% if (done) { %>
      <a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
    <% } %>
    <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
  </script>

  </body>
</html>

 

// An example Backbone application contributed by
// [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple
// [LocalStorage adapter](backbone.localStorage.html)
// to persist Backbone models within your browser.

// Load the application once the DOM is ready, using `jQuery.ready`:
$(function(){

  // Todo Model
  // ----------

  // Our basic **Todo** model has `title`, `order`, and `done` attributes.
  var Todo = Backbone.Model.extend({

    // Default attributes for the todo item.
    defaults: function() {
      return {
        title: "empty todo...",
        order: Todos.nextOrder(),
        done: false
      };
    },

    // Toggle the `done` state of this todo item.
    toggle: function() {
      this.save({done: !this.get("done")});
    }

  });

  // Todo Collection
  // ---------------

  // The collection of todos is backed by *localStorage* instead of a remote
  // server.
  var TodoList = Backbone.Collection.extend({

    // Reference to this collection's model.
    model: Todo,

    // Save all of the todo items under the `"todos-backbone"` namespace.
    localStorage: new Backbone.LocalStorage("todos-backbone"),

    // Filter down the list of all todo items that are finished.
    done: function() {
      return this.where({done: true});
    },

    // Filter down the list to only todo items that are still not finished.
    remaining: function() {
      return this.where({done: false});
    },

    // We keep the Todos in sequential order, despite being saved by unordered
    // GUID in the database. This generates the next order number for new items.
    nextOrder: function() {
      if (!this.length) return 1;
      return this.last().get('order') + 1;
    },

    // Todos are sorted by their original insertion order.
    comparator: 'order'

  });

  // Create our global collection of **Todos**.
  var Todos = new TodoList;

  // Todo Item View
  // --------------

  // The DOM element for a todo item...
  var TodoView = Backbone.View.extend({

    //... is a list tag.
    tagName:  "li",

    // Cache the template function for a single item.
    template: _.template($('#item-template').html()),

    // The DOM events specific to an item.
    events: {
      "click .toggle"   : "toggleDone",
      "dblclick .view"  : "edit",
      "click a.destroy" : "clear",
      "keypress .edit"  : "updateOnEnter",
      "blur .edit"      : "close"
    },

    // The TodoView listens for changes to its model, re-rendering. Since there's
    // a one-to-one correspondence between a **Todo** and a **TodoView** in this
    // app, we set a direct reference on the model for convenience.
    initialize: function() {
      this.listenTo(this.model, 'change', this.render);
      this.listenTo(this.model, 'destroy', this.remove);
    },

    // Re-render the titles of the todo item.
    render: function() {
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.toggleClass('done', this.model.get('done'));
      this.input = this.$('.edit');
      return this;
    },

    // Toggle the `"done"` state of the model.
    toggleDone: function() {
      this.model.toggle();
    },

    // Switch this view into `"editing"` mode, displaying the input field.
    edit: function() {
      this.$el.addClass("editing");
      this.input.focus();
    },

    // Close the `"editing"` mode, saving changes to the todo.
    close: function() {
      var value = this.input.val();
      if (!value) {
        this.clear();
      } else {
        this.model.save({title: value});
        this.$el.removeClass("editing");
      }
    },

    // If you hit `enter`, we're through editing the item.
    updateOnEnter: function(e) {
      if (e.keyCode == 13) this.close();
    },

    // Remove the item, destroy the model.
    clear: function() {
      this.model.destroy();
    }

  });

  // The Application
  // ---------------

  // Our overall **AppView** is the top-level piece of UI.
  var AppView = Backbone.View.extend({

    // Instead of generating a new element, bind to the existing skeleton of
    // the App already present in the HTML.
    el: $("#todoapp"),

    // Our template for the line of statistics at the bottom of the app.
    statsTemplate: _.template($('#stats-template').html()),

    // Delegated events for creating new items, and clearing completed ones.
    events: {
      "keypress #new-todo":  "createOnEnter",
      "click #clear-completed": "clearCompleted",
      "click #toggle-all": "toggleAllComplete"
    },

    // At initialization we bind to the relevant events on the `Todos`
    // collection, when items are added or changed. Kick things off by
    // loading any preexisting todos that might be saved in *localStorage*.
    initialize: function() {

      this.input = this.$("#new-todo");
      this.allCheckbox = this.$("#toggle-all")[0];

      this.listenTo(Todos, 'add', this.addOne);
      this.listenTo(Todos, 'reset', this.addAll);
      this.listenTo(Todos, 'all', this.render);

      this.footer = this.$('footer');
      this.main = $('#main');

      Todos.fetch();
    },

    // Re-rendering the App just means refreshing the statistics -- the rest
    // of the app doesn't change.
    render: function() {
      var done = Todos.done().length;
      var remaining = Todos.remaining().length;

      if (Todos.length) {
        this.main.show();
        this.footer.show();
        this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
      } else {
        this.main.hide();
        this.footer.hide();
      }

      this.allCheckbox.checked = !remaining;
    },

    // Add a single todo item to the list by creating a view for it, and
    // appending its element to the `<ul>`.
    addOne: function(todo) {
      var view = new TodoView({model: todo});
      this.$("#todo-list").append(view.render().el);
    },

    // Add all items in the **Todos** collection at once.
    addAll: function() {
      Todos.each(this.addOne, this);
    },

    // If you hit return in the main input field, create new **Todo** model,
    // persisting it to *localStorage*.
    createOnEnter: function(e) {
      if (e.keyCode != 13) return;
      if (!this.input.val()) return;

      Todos.create({title: this.input.val()});
      this.input.val('');
    },

    // Clear all done todo items, destroying their models.
    clearCompleted: function() {
      _.invoke(Todos.done(), 'destroy');
      return false;
    },

    toggleAllComplete: function () {
      var done = this.allCheckbox.checked;
      Todos.each(function (todo) { todo.save({'done': done}); });
    }

  });

  // Finally, we kick things off by creating the **App**.
  var App = new AppView;

});

 

Backbone 如何實現雙向數據綁定?

http://codedocker.com/backbone-events-introduce/

model to view

var View = Backbone.View.extend({  
    initialize:function(){
        this.listenTo(this.model,'change:name',this.onNameChange);
    },
    onNameChange:function(){
        this.$('.name').text(this.model.get('name'));
    }
    template: '<span class="name"></span>'
});
var m = new Backbone.Model({name:'Jack'});  
var v = new View({model:m});  
m.set('name','John');  

 

view to model

var View = Backbone.View.extend({  
    initialize:function(){
        this.listenTo(this.model,'change:name',this.onNameChange);
    },
    onNameChange:function(){
        this.$('.name').text(this.model.get('name'));
    }
    template: '<input type="text" class="name-input"><span class="name"></span>',
    events: {'input .name-input': '_changeName'},
    _changeName: function(e){
        var value = e.currentTarget.value;
        this.model.set('name', value);
        return false;
    }       
});
var m = new Backbone.Model({name:'Jack'});  
var v = new View({model:m});  
m.set('name','John');  
相關文章
相關標籤/搜索