1. 程式人生 > >淺談js中的MVC

淺談js中的MVC

模擬 ner end i++ 反饋 mov 構架 als 觀察

MVC是什麽?

MVC是一種架構模式,它將應用抽象為3個部分:模型(數據)、視圖、控制器(分發器)

本文將用一個經典的例子todoList來展開

技術分享

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

1、用戶在視圖V上與應用程序交互

2、控制器C觸發相應的事件,要求模型M改變狀態(讀寫數據)

3、模型M將數據發送到視圖V,更新數據,展現給用戶

在js的傳統開發模式中,大多基於事件驅動的:

1、hash驅動

2、DOM事件,用來驅動視圖

3、模型事件(業務模型事件和數據模型事件),用來驅動模型和模型結合

所以js中的MVC的特點是:單向流動、事件驅動

一)模型

模型存放著應用的所有數據對象(業務數據、數據校驗、增刪改查),比如,例子todoList中的store模型,存放每一條記錄與之有關的邏輯

數據時面向對象的,當控制器請求模型讀寫數據時,模型就將數據包裝成模型實例。任何定義在這個數據模型上的函數或邏輯都可以直接被調用。在本文的例子中采用localStorage也是類似道理的。存儲的Todos可以隨時被調用

模型不關心,不包含視圖和控制器的邏輯。它們應該是相互解耦的。這裏提一點,模型與視圖的耦合,顯然是違反MVC架構原則,但往往我們有時候卻因為業務關系而無法完全解耦

模型表現了領域特定的數據,當一個模型有所改變的時候,它會通知它的觀察者

二)視圖

視圖是呈現給用戶的,是用戶交互的第一入口。它定義配置、管理者每個頁面相應的模板與組件,它表現為一個模型的當前狀態,視圖通過觀察者模式監視模型,以獲得最新的數據,來呈現最新的頁面。

所以,頁面首次加載時,往往是從接受模型的數據開始

三)控制器

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

頁面加載完成後,控制器會監聽視圖的用戶交互(按鈕點擊或表單提交),一旦用戶發生交互時,控制器做出視圖的選擇,觸發控制器的事件處理機制,去派發新的事件,通知模型更新數據

Demo-todoList


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

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

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

2、C層監聽用戶的添加與刪除操作,添加是add()函數,他執行了回調函數render,同時向M層寫入數據,通知M層改變,刪除操作同理

3、M層是本地存儲localStorage,模擬一個存儲數據對象的後臺模型

<!DOCTYPE html>
<html lang="en">
<head>
           <meta charset="UTF-8">
            <title>todo</title>
</head>
<body>
<header>
            <h3>待定事項</h3>
</header>
<main>
           <ul id="todoList"></ul>
           <input type="text" id="content">
           <button id="confirm">確認</button>
</main>
<script>
    (function(){
                  const ADD_KEY=_todoList_
                  const Utils={
                   //模擬Modal(實體模型)
               store(key,data)
                if(arguments.length>1){
                  return localStorage.setItem(key,JSON.stringify(data));
}
else{
        let storeData=localStorage.getItem(key);
        return (storeData &&JSON.parse(storeData))||[];//這裏一定要設置初始值為[]
}
}
}
class Todo{
          constructor(id,text=""){
                  this.id=id;
                 this.text=text
}
}
           let App={
                init(){
                       //this.todos為一個存儲json對象的數組,是一個實例化的數據對象,可任意調用
                  this.todos=Utils.store(ADD_KEY)
                  this.findDom()
                  this.bindEvent()
                  this.render()//初始化渲染
        },
                     findDom(){
                this.contentBox=document.querySelector("#content")
this.confirm=document.querySelector("#confirm")
this.todoList=document.querySelector("#todoList")
this.todoListItem=document.getElementByTagName("li")
},
//模擬Controller(業務邏輯層)
bindEvent(){
         this.confirm.addEventListener(click,()=>{
         //要求模型M改變狀態,add()函數是寫入數據操作
         this.add()
 },false)
           this.todoList.addEventListener(click,(item)=>{//時間委托,優化性能 
          this.remove(item)
},false)
},
//這裏勉強抽象成一個視圖吧
view(){
let fragment=document.createDocumentFragment()//減少回流次數
fragment=‘‘
for(let i=0;i<this.todos.length;i++){ //一次性DOM節點生成
//這裏使用拼接字符串代替視圖的模板,
//模板是用一種聲明的方式指定部分甚至所有的視圖對象
fragment +=<li>&{this.todos[i].text}</li>
}
this.todoList.innerHTML=fragment
},
//render()函數作為一個訂閱者的回調函數,數據的變化會反饋到模型store
//換句話說:視圖通過觀察者模式,觀察模型store,當模型發生改變,觸發視圖更新
render(){
   this.view()
Utils.store(ADD_KEY,this.todos)
},
getItemIndex(item){
let itemIndex
if (item.target.tagName.toLowerCase()===li){
let arr=Array.prototype.slice.call(this.todoListItem)
let index=arr.indexOf(item.target)
return itemIndex=index
}
},
add(e){
 let id=Number(new Date())
let text=this.contentBox.value
let addTodo=new Todo(id,text)
this.todos.unshift(addTodo)//模型發生改變
this.render()//當模型發生改變,觸發視圖更新
},
remove(item){
let index=this.getItemIndex(item)
this.todos.splice(index,1)
this.render()
}
}
App.init()
})()
</script>
</body>
</html>

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

MVC模式的優點

MVC編程把所有精力放在數據處理,盡可能減少對網頁元素的處理。對於有一定數量功能的網頁,MVC模式下強制規範代碼,簡化,減少重復代碼,使代碼易於擴充

MVC模式的弊端

1、清晰的構架以代碼的復雜性為代價,對小項目反而降低開發效率

2、控制層和視圖層耦合,導致沒有真正分離和重用

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

總結:其實,現在MVC在前端用得比較少了,因為它的局限性,催生了MVVM模式的流行與廣泛使用

淺談js中的MVC