MV*模式
- MVC
- MVP
- MVVM
1 MVC
一個應用分為三部分
-
模型 (Model):數據保存
應用程序的
數據、控制與修改這些數據的業務規則
Model改變時:通知View,為View提供查詢Model相關狀態的能力,為Controller提供訪問封裝在Model內部的應用程序功能的能力。 -
視圖 (View):用户界面
組織Model的內容
Model改變時:View負責維護數據表現的一致性,同時View將用户的請求通知Controller -
控制器 (Controller):業務邏輯
應用程序的行為
解釋來自View的用户請求,把請求映射為行為,再由Model實現這些行為。
結構圖:
- View傳送指令到Controller
- Controller完成業務邏輯後,要求Model改變狀態
-
Model將新的數據發送到View,用户得到反饋
接受指令的方式
由View接受指令,傳遞給Controller
Controller直接接受指令
2 MVP
- 模型(Model):提供數據
- 視圖(View):用户界面
- 表示器(Presenter):邏輯的處理
結構圖
- View與Model無聯繫,都通過Presenter傳遞
- View中不部署任何業務邏輯 - 被動視圖
- 所有邏輯都部署在Presenter
與MVC的區別
View不能直接從Model中讀取數據
3 MVVM
基本上與MVP模式一致
- 模型(Model):保存數據
- 視圖(View):用户界面
-
數據驅動(View-Model):業務邏輯
VM負責轉換Model中的數據對象
結構圖
- 操作View時,ViewModel感知變化,通知Model發生相應的變化,若Model改變時,ViewModel感知變化,通知View進行更新
- ViewModel與View雙向數據綁定,Model通過接口請求數據交互,承上啓下。
雙向數據綁定
- Vue2:Object.defineProperty()
- Vue3:Proxy代理
1 Vue2雙向綁定實現
Object.defineProperty(obj,prop,description)
原理簡析,不做依賴收集
/**
* 對Object.defineProperty()進行封裝
*/
function defineReactive(obj, key, value) {
//遞歸 - 對象的屬性仍是對象
observe(value);
//變化偵測
Object.defineProperty(obj, key, {
get() {
return value;
},
set(newVal) {
if (newVal !== value) {
updateView();
value = newVal;
observe(newVal)
}
}
})
}
/**
* 對一個對象所有屬性的變化偵測
*/
function observe(target) {
//非對象,直接返回
if (typeof target !== 'object') {
return target;
}
//將每個屬性轉換為getter和setter形式
for (let key in target) {
defineReactive(target, key, target[key])
}
}
//模擬更新視圖的方法
function updateView() {
console.log("更新視圖");
}
通過直接調用observe偵測對象屬性的變化
存在的問題
- 性能較差
- 對象上新增屬性無法偵測
- 改變數組的length屬性無法被偵測
2 Vue3雙向綁定實現
Proxy是一種可以攔截並改變底層JavaScript引擎操作的包裝器,性能更優異
數組可以像對象一樣觸發get與set
【js】代理與反射(Proxy/Reflect)
【阮一峯】ES6標準-Proxy
原理簡析,不做依賴收集
基本流程
cosnt o = {name:"張三"}
const proxy = new Proxy(o,{
get(target,key,receiver){
console.log("讀取屬性值")
},
set(target,key,value,receiver){
console.log("設置屬性值")
},
deleteProperty(target,key){
console.log("刪除屬性")
}
})
proxy.name; //讀取屬性值
proxy.name = "李四";//設置屬性值
delete proxy[name] ;//刪除屬性
自定義的業務邏輯
//判斷是否是對象
function isObj(val) {
return val !== null && typeof val === "object"
}
//判斷當前對象是否有指定屬性
function hasOwn(target, key) {
return target.hasOwnProperty(key)
}
//存儲代理信息
const toProxy = new WeakMap()
const toRaw = new WeakMap()
/**
*創建響應式對象
*/
function createReactiveObj(target) {
//目標不是對象,直接返回target
if (!isObj(target)) {
return target
}
const proxy = toProxy.get(target)
//如果目標對象已被代理,直接返回代理對象
if (proxy) {
return proxy
}
//如果目標對象是代理對象,並有對應的真的對象,直接返回
if (toRaw.has(target)) {
return target
}
//生成代理對象
const observed = new Proxy(target, {
get(target, key, receiver) {
console.log("讀取值");
const result = Reflect.get(target, key, receiver)
//為返回值添加代理
return isObj(result) ? reactive(result) : result
},
set(target, key, value, receiver) {
//判斷目標對象是否已經存在該屬性
const hasProperty = hasOwn(target, key)
const oldVal = Reflect.get(target, key)
if (!hasProperty) {
console.log("新增屬性");
} else if (oldVal !== value) {
console.log("修改屬性");
}
return Reflect.set(target, key, value, receiver)
},
deleteProperty(target, key) {
console.log("刪除值");
return Reflect.deleteProperty(target, key)
}
})
//添加目標對象與代理對象到Map
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
//響應式入口
function reactive(target) {
return createReactiveObj(target)
}
與基本流程相比,自定義實現的功能
-
解決多層對象偵測的問題,
get中判斷
-
多次代理
用WeakMap存儲代理信息,判斷是否已經被代理,或者本身是代理對象
-
數組push發生兩次get,一次屬性一次length
新舊值對比,在push元素後length已經改變,第二次的get不對length做任何修改,避免了添加一個值,視圖更新兩次
Proxy的handler中還可以寫很多方法,以滿足複雜的業務