Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Understanding the MVC,MVP,MVVM in JavaScript

Understanding the MVC,MVP,MVVM in JavaScript

JSDC 2013 Slides

大澤木小鐵

May 19, 2013
Tweet

More Decks by 大澤木小鐵

Other Decks in Technology

Transcript

  1. UNDERSTANDING THE
    MVC
    MVP
    MVVM
    IN JAVASCRIPT

    View full-size slide

  2. 咦?默念?

    View full-size slide

  3. Who am I ?
    I am Iron Man

    View full-size slide

  4. Who am I ?
    I am Iron Man
    閃到腰的

    View full-size slide

  5. Jace Ju
    ⼤大澤⽉⽊木⼩小鐵

    View full-size slide

  6. ⺫⽬目前正在領
    微博的薪⽔水

    View full-size slide

  7. 我不是前端工程師...

    View full-size slide

  8. 我不是前端工程師...
    我也不是社工...

    View full-size slide

  9. 我不是前端工程師...
    我也不是社工...
    我只是個打雜的...

    View full-size slide

  10. 我不是前端工程師...
    我也不是社工...
    我只是個打雜的...
    System, DBA, Backend...
    HTML, CSS, JavaScript...
    我都只會一點點... Orz

    View full-size slide

  11. 雖然我是這次
    講師群裡最弱的

    View full-size slide

  12. 雖然我是這次
    講師群裡最弱的
    但我有 GitHub
    https://github.com/jaceju

    View full-size slide

  13. 不過我喜歡分享
    自己學到的給大家
    然後也從大家
    那邊學到很棒的東西
    ^ ^

    View full-size slide

  14. 20 年前...

    View full-size slide

  15. 20 年前...
    駭客正夯

    View full-size slide

  16. 20 年前...
    駭客正夯
    GUI (圖形化操作介面)

    View full-size slide

  17. 視覺控制項與資料整合
    強化使⽤用者對資料的印象
    事件驅動
    配合 Framework / IDE
    GUI

    View full-size slide

  18. 常常會在事件處理⽅方法裡
    包含了資料運算邏輯
    每個元件之間的互動
    都放在⼀一個⽅方法裡
    問題

    View full-size slide

  19. MVC / MVP / MVVM
    就是要解決這些問題

    View full-size slide

  20. 瞭解 MVC / MVP / MVVM
    的基本原理
    JavaScript 如何實作它們
    所以今天...

    View full-size slide

  21. MV* ⺫⽬目的
    視覺邏輯
    流程邏輯 資料運算邏輯

    View full-size slide

  22. 從資料運算邏輯中分離視覺邏輯
    MV* ⺫⽬目的
    視覺邏輯
    流程邏輯 資料運算邏輯

    View full-size slide

  23. 從資料運算邏輯中分離視覺邏輯
    從視覺邏輯中分離流程邏輯
    MV* ⺫⽬目的
    視覺邏輯
    流程邏輯 資料運算邏輯

    View full-size slide

  24. 分離關注點
    團隊合作
    便於測試
    推卸責任

    View full-size slide

  25. 團隊合作
    便於測試
    推卸責任
    分離關注點
    釐清問題點

    View full-size slide

  26. Example
    0px
    + -
    span#num
    button#increase button#decrease

    View full-size slide

  27. Code
    http://jsfiddle.net/EWbxg/

    View full-size slide

  28. var myapp = {};
    myapp.Model = function () {
    var val = 0;
    this.add = function (v) {
    if (val < 100) val += v;
    };
    this.sub = function (v) {
    if (val > 0) val -= v;
    };
    this.getVal = function () {
    return val;
    };
    };
    Model
    資料的狀態

    View full-size slide

  29. var myapp = {};
    myapp.Model = function () {
    var val = 0;
    this.add = function (v) {
    if (val < 100) val += v;
    };
    this.sub = function (v) {
    if (val > 0) val -= v;
    };
    this.getVal = function () {
    return val;
    };
    };
    Model
    操作內部狀態的⾏行為

    View full-size slide

  30. myapp.View = function () {
    var $num = $('#num');
    var $incBtn = $('#increase');
    var $decBtn = $('#decrease');
    this.render = function (model) {
    $num.text(model.getVal() + 'px');
    };
    };
    View
    管理視覺元件

    View full-size slide

  31. View
    myapp.View = function () {
    var $num = $('#num');
    var $incBtn = $('#increase');
    var $decBtn = $('#decrease');
    this.render = function (model) {
    $num.text(model.getVal() + 'px');
    };
    };
    呈現資料

    View full-size slide

  32. 由 Xerox PARC 提出
    MVC 簡史
    http://huoding.com/2011/05/02/64
    MVC 簡介

    View full-size slide

  33. View 觀察 Model
    Controller 負責派送 Request
    並控制 Model 的狀態
    使⽤用者接觸的對象是
    Controller
    MVC 架構
    看報紙才知道

    View full-size slide

  34. View
    Controller
    Model

    View full-size slide

  35. (箭頭⽅方向表⽰示擁有該物件參考)
    View
    Controller
    Model
    Controller 初始化
    Model 與 View

    View full-size slide

  36. View
    Controller
    Model
    View 向 Model 註冊

    View full-size slide

  37. View
    Controller
    Model
    使⽤用者看到 View
    的初始輸出

    View full-size slide

  38. View
    Controller
    Model
    使⽤用者向 Controller
    發出請求

    View full-size slide

  39. View
    Controller
    Model
    Controller 改變
    Model 狀態
    Model 再次
    通知 View

    View full-size slide

  40. View
    Controller
    Model
    使⽤用者看到 View
    改變了輸出

    View full-size slide

  41. Code
    http://jsfiddle.net/uVBvq/

    View full-size slide

  42. myapp.Model = function () {
    // ...
    var views = [];
    this.register = function (view) {
    views.push(view);
    }
    var self = this;
    this.notify = function () {
    for (var i = 0;
    i < views.length; i++) {
    views[i].render(self);
    }
    };
    };
    Model
    Model 擁有 View 的參考

    View full-size slide

  43. myapp.Model = function () {
    // ...
    var views = [];
    this.register = function (view) {
    views.push(view);
    }
    var self = this;
    this.notify = function () {
    for (var i = 0;
    i < views.length; i++) {
    views[i].render(self);
    }
    };
    };
    Model
    Model 有變化時通知 View 更新

    View full-size slide

  44. View
    myapp.View = function (controller) {
    // ...
    $incBtn.click(controller.increase);
    $decBtn.click(controller.decrease);
    };
    引⽤用 Controller 參考

    View full-size slide

  45. myapp.View = function (controller) {
    // ...
    $incBtn.click(controller.increase);
    $decBtn.click(controller.decrease);
    };
    View
    將事件綁定在
    Controller 的 Action

    View full-size slide

  46. myapp.Controller = function () {
    var model = null;
    var view = null;
    this.init = function () {
    model = new myapp.Model();
    view = new myapp.View(this);
    model.register(view);
    model.notify();
    };
    };
    Controller
    Controller 初始化
    Model 與 View

    View full-size slide

  47. myapp.Controller = function () {
    var model = null;
    var view = null;
    this.init = function () {
    model = new myapp.Model();
    view = new myapp.View(this);
    model.register(view);
    model.notify();
    };
    };
    Controller
    View 向 Model
    註冊⾃自⼰己

    View full-size slide

  48. myapp.Controller = function () {
    // ...
    this.increase = function () {
    model.add(1);
    model.notify();
    };
    this.decrease = function () {
    model.sub(1);
    model.notify();
    };
    };
    Controller
    Controller 負責控制 Model

    View full-size slide

  49. (function () {
    var controller = new myapp.Controller();
    controller.init();
    })();
    Run
    外界只接觸到 Controller

    View full-size slide

  50. Model 不應該擁有 View 的參考
    View 不容易測試
    缺點

    View full-size slide

  51. MVP
    叫我嗎?

    View full-size slide

  52. 由 IBM 提出
    Microsoft ASP.Net
    WebForms
    MVP 簡介

    View full-size slide

  53. 使⽤用者接觸的對象是 View
    Presenter 取代 Controller
    派送的⾓角⾊色由 View 來扮演
    MVP 結構

    View full-size slide

  54. Supervising Controller
    Passive View
    MVP 類型

    View full-size slide

  55. Passive View
    View 和 Model 不需要知道對⽅方
    Presenter 的⼯工作量⽐比較⼤大
    整體的可測試⽐比較好

    View full-size slide

  56. View
    Presenter
    Model

    View full-size slide

  57. View
    Model
    Presenter
    使⽤用者看到 View
    的初始輸出
    View 初始化 Presenter

    View full-size slide

  58. View
    Model
    Presenter
    Presenter 初始化 Model

    View full-size slide

  59. View
    Model
    Presenter
    View 委派 Presenter 來
    處理使⽤用者的請求
    使⽤用者透過 View
    來發出請求

    View full-size slide

  60. Persenter 改變
    並獲取 Model 狀態
    View
    Model
    Presenter

    View full-size slide

  61. 使⽤用者看到 View
    改變了輸出
    View
    Model
    Presenter
    Presenter
    將 Model 的狀態
    更新到 View 上⾯面

    View full-size slide

  62. 簡單來說
    要在發生某個事件後
    我們才會知道
    總統是隻水母

    View full-size slide

  63. Code
    http://jsfiddle.net/tgHra/

    View full-size slide

  64. myapp.Model = function () {
    var val = 0;
    this.add = function (v) {
    if (val < 100) val += v;
    };
    this.sub = function (v) {
    if (val > 0) val -= v;
    };
    this.getVal = function () {
    return val;
    };
    };
    Model
    不需要觀察者模式

    View full-size slide

  65. myapp.Presenter = function (view) {
    var _model = new myapp.Model();
    var _view = view;
    _view.render(_model.getVal());
    this.increase = function () {
    _model.add(1);
    _view.render(_model.getVal());
    };
    this.decrease = function () {
    _model.sub(1);
    _view.render(_model.getVal());
    };
    };
    Presenter
    Presenter 將
    Model 內容
    傳遞給 View

    View full-size slide

  66. myapp.View = function () {
    // ...
    this.init = function () {
    var presenter = new myapp.Presenter(this);
    $incBtn.click(presenter.increase);
    $decBtn.click(presenter.decrease);
    };
    }
    View
    View 將請求委派給 Presenter

    View full-size slide

  67. (function () {
    var view = new myapp.View();
    view.init();
    })();
    Run
    外界只接觸到 View

    View full-size slide

  68. 與 MVC 差異
    適合維持狀態
    View 與 Model 之間解耦

    View full-size slide

  69. 與 MVC 差異
    適合維持狀態
    View 與 Model 之間解耦

    View full-size slide

  70. 由 Microsoft 提出
    Microsoft WPF
    Knockout.js
    http://blog.darkthread.net/
    post-2012–05–09-knockout-js-intro.aspx
    MVVM 簡介

    View full-size slide

  71. ViewModel :
    將資料轉換成視覺元件可⽤用的狀態
    View : 包含 bind 邏輯的樣版
    Model : 跟資料來源溝通⽤用
    MVVM 架構

    View full-size slide

  72. ViewModel ⼀一有變化就更新 View
    View ⼀一有輸⼊入值就更新 ViewModel
    Data-Binding 由 Framework 來處理
    不必⾃自⼰己找元素
    Two-way
    Data Binding

    View full-size slide

  73. Code
    http://jsfiddle.net/fVAcE/

    View full-size slide



  74. +
    -
    View (knockout.js)
    View (Template) 透過 HTML
    屬性來與 ViewModel 結合

    View full-size slide

  75. myapp.ViewModel = function () {
    var model = new myapp.Model();
    this.num = ko.observable(model.getVal());
    this.val = ko.computed(function() {
    return this.num() + 'px';
    }, this);
    };
    ViewModel
    View 利⽤用觀察者模式
    將 ViewModel 結合起來

    View full-size slide

  76. myapp.ViewModel = function () {
    // ...
    this.increase = function () {
    model.add(1);
    this.num(model.getVal());
    };
    this.decrease = function () {
    model.sub(1);
    this.num(model.getVal());
    };
    };
    ko.applyBindings(new myapp.ViewModel());
    ViewModel
    ViewModel 操作 Model
    並將資料 binding 到 View 元素上

    View full-size slide

  77. myapp.ViewModel = function () {
    // ...
    this.increase = function () {
    model.add(1);
    this.num(model.getVal());
    };
    this.decrease = function () {
    model.sub(1);
    this.num(model.getVal());
    };
    };
    ko.applyBindings(new myapp.ViewModel());
    ViewModel
    透過 Framework 結合
    View 與 ViewModel

    View full-size slide

  78. 需要隨時監控 View 的狀態
    效能會受到影響
    效能掌握在 Framework ⼿手上
    Data-Binding 的錯誤要在
    Runtime 時才能發現
    MVVM 缺點

    View full-size slide

  79. ⽤用事件代替引⽤用,
    解耦物件之間的關係
    維繫事件與 callbacks
    射後不理
    Event 概念

    View full-size slide

  80. callback
    Object Event
    callback
    callback
    註冊事件對應的
    callback
    (on / bind)

    View full-size slide

  81. Object Event
    物件觸發事件
    (trigger)
    callback
    callback
    callback

    View full-size slide

  82. Object Event
    Event 呼叫事件
    對應的 callback
    callback
    callback
    callback

    View full-size slide

  83. Code
    http://jsfiddle.net/pm3xr/
    從 Backbone.js
    抽出來簡化

    View full-size slide

  84. var Event = {
    _events: {},
    on: function(name, callback, context) {
    ! this._events || (this._events = {});
    ! var events = this._events[name]
    ! !
    ! || (this._events[name] = []);
    ! events.push({
    ! ! callback: callback,
    ! ! ctx: context
    ! });
    }
    };
    Event.on
    將 callback 及 context
    加到事件的庫裡

    View full-size slide

  85. Event.trigger
    var Event = {
    // ...
    trigger: function(name) {
    !
    var events = this._events[name];
    !
    for (var i = 0; i < events.length; i++) {
    !
    ! (ev = events[i]).callback.call(ev.ctx);
    !
    }
    }
    };
    將事件對應的 callback
    ⼀一⼀一找出來執⾏行

    View full-size slide

  86. Event.on('event1', function () {
    ! alert('callback 1');
    }, this);
    Event.on('event1', function () {
    ! alert('callback 2');
    }, this);
    Event.on('event2', function () {
    ! alert('callback 3');
    }, this);
    Event.trigger('event1');
    Event.trigger('event2');
    Event Demo
    事件綁定 callback

    View full-size slide

  87. Event.on('event1', function () {
    ! alert('callback 1');
    }, this);
    Event.on('event1', function () {
    ! alert('callback 2');
    }, this);
    Event.on('event2', function () {
    ! alert('callback 3');
    }, this);
    Event.trigger('event1');
    Event.trigger('event2');
    Event Demo
    觸發事件

    View full-size slide

  88. 取代了觀察者模式
    Model, View 透過 Event 溝通
    Controller, Presenter,
    ViewModel 可以更靈活
    隱藏在 Framework 裡
    Event + MV*

    View full-size slide

  89. 觀看的⾓角度與實作的⽅方式
    邏輯分離
    模組化
    各位還是⽤用 Framework 吧
    Points

    View full-size slide

  90. 前端工程...
    會跟著各位一起成長的

    View full-size slide