動態

詳情 返回 返回

【vue】MVVM、雙向綁定 - 動態 詳情

MV*模式

  1. MVC
  2. MVP
  3. MVVM

1 MVC

一個應用分為三部分

  1. 模型 (Model):數據保存

    應用程序的數據、控制與修改這些數據的業務規則
    Model改變時:通知View,為View提供查詢Model相關狀態的能力,為Controller提供訪問封裝在Model內部的應用程序功能的能力。
  2. 視圖 (View):用户界面

    組織Model的內容
    Model改變時:View負責維護數據表現的一致性,同時View將用户的請求通知Controller
  3. 控制器 (Controller):業務邏輯

    應用程序的行為
    解釋來自View的用户請求,把請求映射為行為,再由Model實現這些行為。

結構圖:
image.png

  • View傳送指令到Controller
  • Controller完成業務邏輯後,要求Model改變狀態
  • Model將新的數據發送到View,用户得到反饋

    接受指令的方式
    由View接受指令,傳遞給Controller
    Controller直接接受指令

2 MVP

  1. 模型(Model):提供數據
  2. 視圖(View):用户界面
  3. 表示器(Presenter):邏輯的處理

結構圖
image.png

  • View與Model無聯繫,都通過Presenter傳遞
  • View中不部署任何業務邏輯 - 被動視圖
  • 所有邏輯都部署在Presenter
與MVC的區別
View不能直接從Model中讀取數據

3 MVVM

基本上與MVP模式一致

  1. 模型(Model):保存數據
  2. 視圖(View):用户界面
  3. 數據驅動(View-Model):業務邏輯

    VM負責轉換Model中的數據對象

結構圖
image.png

  • 操作View時,ViewModel感知變化,通知Model發生相應的變化,若Model改變時,ViewModel感知變化,通知View進行更新
  • ViewModel與View雙向數據綁定,Model通過接口請求數據交互,承上啓下。

雙向數據綁定

  1. Vue2:Object.defineProperty()
  2. 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偵測對象屬性的變化

存在的問題

  1. 性能較差
  2. 對象上新增屬性無法偵測
  3. 改變數組的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中還可以寫很多方法,以滿足複雜的業務

user avatar littlelyon 頭像 linx 頭像 hard_heart_603dd717240e2 頭像 u_17443142 頭像 shuirong1997 頭像 jiavan 頭像 assassin 頭像 nznznz 頭像 congjunhua 頭像 user_ze46ouik 頭像 54r9rxzy 頭像 yanyue404 頭像
點贊 43 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.