MVC是什么?

MVC是一種架構(gòu)模式,它將應(yīng)用抽象為3個部分:模型(數(shù)據(jù))、視圖、控制器(分發(fā)器)。

本文將用一個經(jīng)典的例子todoList來展開(代碼在最后)。

 大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

一個事件發(fā)生的過程(通信單向流動):

1、用戶在視圖 V 上與應(yīng)用程序交互

2、控制器 C 觸發(fā)相應(yīng)的事件,要求模型 改變狀態(tài)(讀寫數(shù)據(jù))

3、模型 M 將數(shù)據(jù)發(fā)送到視圖 ,更新數(shù)據(jù),展現(xiàn)給用戶

 

js傳統(tǒng)開發(fā)模式,大多基于事件驅(qū)動的

1、hash驅(qū)動

2、DOM事件,用來驅(qū)動視圖

3、模型事件(業(yè)務(wù)模型事件和數(shù)據(jù)模型事件),用來驅(qū)動模型和模型結(jié)合

所以js中的mvc的特點是:單向流動、事件驅(qū)動

 

一)模型

模型存放應(yīng)用的所有數(shù)據(jù)對象業(yè)務(wù)數(shù)據(jù)、數(shù)據(jù)校驗、增刪改查),比如,例子todoList中的store模型,存放每一條記錄與之有關(guān)的邏輯。

數(shù)據(jù)是面向?qū)ο蟮?,當控制?/span>請求模型讀寫數(shù)據(jù)時,模型就將數(shù)據(jù)包裝成模型實例。任何定義在這個數(shù)據(jù)模型上的函數(shù)或邏輯都可以直接被調(diào)用。在本文的例子中采用localSrorage也是類似道理的。存儲的Todos可以隨時被調(diào)用

模型不關(guān)心,不包含視圖和控制器的邏輯。它們應(yīng)該是互相解耦的。這里提一點,模型視圖的耦合,顯然是違反MVC架構(gòu)原則,但往往我們有時候卻因為業(yè)務(wù)關(guān)系而無法完全解耦

模型表現(xiàn)了領(lǐng)域特定的數(shù)據(jù),當一個模型有所改變的時候它會通知它的觀察者(視圖)。

 

二)視圖

視圖是呈現(xiàn)給用戶的,是用戶交互的第一入口。它定義配置管理著每個頁面相應(yīng)的模板與組件,它表現(xiàn)一個模型的當前狀態(tài)視圖通過觀察者模式監(jiān)視模型,以獲得最新的數(shù)據(jù),來呈現(xiàn)最新的頁面。所以,頁面首次加載時,往往是從接收模型的數(shù)據(jù)開始。

 

三)控制器

控制器分發(fā)器),是模型和視圖之間的橋梁集中式配置和管理事件分發(fā)、模型分發(fā)、視圖分發(fā),還用來權(quán)限控制、異常處理等。我們的應(yīng)用中往往是有多個控制器的

頁面加載完成后,控制器監(jiān)聽視圖的用戶交互按鈕點擊或表單提交,一旦用戶發(fā)生交互時控制器做出對視圖的選擇,觸發(fā)控制器的事件處理機制,去派發(fā)新的事件,通知模型更新數(shù)據(jù)(這樣就回到了第一步了)

 

Demo-todoList

最后這里是一個用原生js寫的todoLIst,這個demo做的很簡陋,點擊輸入文字點擊確定就添加,刪除是直接點擊該行信息。

 

單獨分離開來舉例子不好講,所以在代碼中進行注釋。首先簡單理下下邊代碼的思路:

1、V層定義配置了一個顯示數(shù)據(jù)的字符串模板,同時定義一個訂閱者的回調(diào)函數(shù)render() 用于頁面更新數(shù)據(jù)。

2、C層監(jiān)聽用戶的添加與刪除操作,添加是add() 函數(shù) 它執(zhí)行了回調(diào)函數(shù)render,同時向M層寫入數(shù)據(jù),通知M層改變。刪除操作同理。

3、M層是本地存儲localStorage,模擬一個存儲數(shù)據(jù)對象的后臺模型。

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

  1 <!DOCTYPE html>  2 <html lang="en">  3 <head>  4     <meta charset="UTF-8">  5     <title>todo</title>  6 </head>  7 <body>  8 <header>  9     <h3>待定事項</h3> 10 </header> 11 <main> 12     <ul id="todoList"></ul> 13     <input type="text" id="content"> 14     <button id="confirm">確認</button> 15 </main> 16  17 <script> 18   (function () { 19     const ADD_KEY = '__todoList__' 20  21     const Utils = { 22       // 模擬 Modal(實體模型) 23       store(key, data) { 24         if (arguments.length > 1) { 25           return localStorage.setItem(key, JSON.stringify(data)); 26         } else { 27           let storeData = localStorage.getItem(key); 28           return (storeData && JSON.parse(storeData)) || []; // 這里一定要設(shè)置初始值為 [] 29         } 30       } 31     } 32  33     class Todo { 34       constructor(id, text = "") { 35         this.id = id 36         this.text = text 37       } 38     } 39  40     let App = { 41       init() { 42         // this.todos 為一個存儲json對象的數(shù)組, 是一個實例化的數(shù)據(jù)對象,可任意調(diào)用 43         this.todos = Utils.store(ADD_KEY) 44         this.findDom() 45         this.bindEvent() 46         this.render() // 初始化渲染 47       }, 48  49  50       findDom() { 51         this.contentBox = document.querySelector("#content") 52         this.confirm = document.querySelector("#confirm") 53         this.todoList = document.querySelector("#todoList") 54         this.todoListItem = document.getElementsByTagName("li") 55       }, 56  57       // 模擬 Controller (業(yè)務(wù)邏輯層) 58       bindEvent() { 59         this.confirm.addEventListener('click', () => { 60           // 要求模型 M 改變狀態(tài),add()函數(shù)是寫入數(shù)據(jù)操作 61           this.add() 62         }, false) 63  64         this.todoList.addEventListener('click', (item) => {  // 事件委托,優(yōu)化性能 65           this.remove(item) 66         }, false) 67       }, 68  69       // 這里勉強抽象成一個視圖吧!!! 70       view() { 71         let fragment = document.createDocumentFragment()   // 減少回流次數(shù) 72         fragment = '' 73  74         for (let i = 0; i < this.todos.length; i++) { // 一次性DOM節(jié)點生成 75           // 這里使用拼接字符串代替視圖的模板, 76           // *******注意模板并不是一個視圖,模板是由視圖定義配置出來的,并被其管理著******* 77           // 模板是用一種聲明的方式指定部分甚至所有的視圖對象 78           fragment += `<li>${this.todos[i].text}</li>` 79         } 80         this.todoList.innerHTML = fragment 81       }, 82  83       // render()函數(shù)作為一個訂閱者的回調(diào)函數(shù),數(shù)據(jù)的變化會反饋到模型 store 84       // 換句話說:視圖通過觀察者模式,觀察模型 store,當模型發(fā)生改變,觸發(fā)視圖更新 85       render() { 86         this.view() 87  88         /** 89          * 這里需要特別提一下,按照 MVC 原則這里本不應(yīng)該出現(xiàn)下面的代碼的 90          * 因為業(yè)務(wù)邏輯關(guān)系(我本地存儲使用的是同一個key值,再次寫入數(shù)據(jù)會覆蓋原來的數(shù)據(jù),), 91          * 所以必須通知模型 M 保存數(shù)據(jù), V 層處理了不該它處理的邏輯,導(dǎo)致 M 與 V 耦合 92          * 93          * 解決辦法是:將其抽象出來編寫一個 視圖助手 helper 94          */ 95         Utils.store(ADD_KEY, this.todos) 96       }, 97  98       getItemIndex(item) { 99         let itemIndex100         if (item.target.tagName.toLowerCase() === 'li') {101           let arr = Array.prototype.slice.call(this.todoListItem)102           let index = arr.indexOf(item.target)103           return itemIndex = index104         }105       },106 107       add(e) {108         let id = Number(new Date())109         let text = this.contentBox.value110         let addTodo = new Todo(id, text)111         this.todos.unshift(addTodo) // 模型發(fā)生改變112         this.render()  // 當模型發(fā)生改變,觸發(fā)視圖更新113       },114 115       remove(item) {116         let index = this.getItemIndex(item)117         this.todos.splice(index, 1)118         this.render()119       }120     }121 122     App.init()123   })()124 </script>125 </body>126 </html>

大學生就業(yè)培訓,高中生培訓,在職人員轉(zhuǎn)行培訓,企業(yè)團訓

隨著界面和邏輯的復(fù)雜,用js或者jq去控制DOM不現(xiàn)實的。上邊例子只是用原生js模擬mvc的思想實現(xiàn)過程。真正地項目往往會依賴一些封裝好的優(yōu)秀庫進行高效開發(fā)。

 

mvc模式的優(yōu)點

mvc編程把所有精力放在數(shù)據(jù)處理,盡可能減少對網(wǎng)頁元素的處理。對于一定數(shù)量功能的網(wǎng)頁,Mvc模式下強制規(guī)范代碼,簡化減少重復(fù)代碼,使代碼易于擴充。

 

mvc模式的弊端

1、清晰的構(gòu)架以代碼的復(fù)雜性為代價, 對小項目反而降低開發(fā)效率。 (如果本文的例子todoList用面條式代碼編寫,那得多簡單啊?。。。?br/>2、控制層和視圖層耦合,導(dǎo)致沒有真正分離和重用 

3、在同一業(yè)務(wù)邏輯下,如果存在多種視圖呈現(xiàn),需要視圖定義配置多個模板引擎、數(shù)據(jù)解析,多次處理數(shù)據(jù)與頁面更新。代碼就充滿了各種選擇器與事件回調(diào),隨著業(yè)務(wù)的膨脹,變得難以維護。

 

總結(jié):其實,現(xiàn)在MVC在前端用得比較少了,因為它的局限性,催生了MVVM模式的流行與廣泛使用,在下篇文章我會談?wù)勎覍?/span>MVVM的理解,以及為何我使用基于MVVM模式的vue框架來高效開發(fā)。

https://github.com/LYZ0106/

http://www.cnblogs.com/LIUYANZUO/p/7231703.html