欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue修改數(shù)據(jù)視圖更新原理學(xué)習(xí)

 更新時(shí)間:2023年11月29日 08:42:12   作者:HUMILITY  
這篇文章主要為大家介紹了vue修改數(shù)據(jù)視圖更新原理學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

MVVM模式

在vue使用中,我們只要修改了數(shù)據(jù),所見視圖便會進(jìn)行相應(yīng)更新。

為什么?原理是什么?

在這期間做三件事(發(fā)布訂閱者模式)

  • 數(shù)據(jù)劫持
  • 依賴收集
  • 派發(fā)更新

Object.defineProperty()詳解

詳見:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Refer...該方法有三個入?yún)?/p>

obj 要定義屬性的對象。

prop 一個字符串或 Symbol,指定了要定義或修改的屬性鍵。

descriptor 要定義或修改的屬性的描述符。

通過該方法進(jìn)行賦值(無需關(guān)注隱藏屬性)

// 定義一個對象
var obj = {}
// 通過Object.defineProperty對obj進(jìn)行賦值
Object.defineProperty(obj, 'name', {
 value: '張三'
})
console.log(obj) // {name: '張三'}
console.log(obj.name) // 張三

引入get

// 定義一個對象
var obj = {}
// 通過Object.defineProperty對obj進(jìn)行賦值
Object.defineProperty(obj, 'name', {
 get() {
     console.log('正在訪問name') // 當(dāng)訪問時(shí),會執(zhí)行該代碼
 }
})
Object.defineProperty(obj, 'age', {
 value: 18
})
console.log(obj.name) // undefined 因?yàn)闆]有被賦值、所以認(rèn)為不存在
console.log(obj.age) // 18
console.log(obj) // {age: 18}

引入set

// 定義一個對象
var obj = {}
// 通過Object.defineProperty對obj進(jìn)行賦值
Object.defineProperty(obj, 'name', {
 get() {
     console.log('正在訪問name') // 當(dāng)訪問時(shí),會執(zhí)行該代碼
 },
 set() {
     console.log('嘗試改變name') // 當(dāng)對name進(jìn)行更改時(shí),執(zhí)行該行代碼
 }
})
Object.defineProperty(obj, 'age', {
 value: 18
})
console.log(obj.name) // undefined
obj.name = '李四'
console.log(obj.name) // undefined 雖然改變了name,
                      // 但并沒有進(jìn)行具體的賦值,此時(shí)會觸發(fā)set內(nèi)代碼

如何賦值?

// 定義一個對象
var obj = {}
// 通過Object.defineProperty對obj進(jìn)行賦值
Object.defineProperty(obj, 'name', {
 // getter return出的值為name值,
 get() {
     console.log('正在訪問name') // 當(dāng)訪問時(shí),會執(zhí)行該代碼
     return 'TEST' // 便于理解我們寫死
 },
 set(newValue) {
     console.log('嘗試改變name', newValue) // 當(dāng)對name進(jìn)行更改時(shí),執(zhí)行該行代碼
 }
})

console.log(obj.name) // 輸出TEST,
obj.name = '李四' // setter內(nèi)輸出 '李四'
console.log(obj.name) // 輸出TEST、因?yàn)樵趃etter中賦值為TEST,setter未成功賦值

引入defineReactive()

//為什么引入該函數(shù)?
//因?yàn)間et和set不好用,并且不希望引入變量進(jìn)行周轉(zhuǎn),例子:
// 定義一個對象
var obj = {}
// 定義一個變量
var temp
// 通過Object.defineProperty對obj進(jìn)行賦值
Object.defineProperty(obj, 'name', {
 // getter return出的值為name值,
 get() {
     console.log('正在訪問name') // 當(dāng)訪問時(shí),會執(zhí)行該代碼
     return temp // 返回temp,此時(shí)已被賦值
 },
 set(newValue) {
     temp = newValue // 賦值給全局變量
     console.log('嘗試改變name', newValue) // 當(dāng)對name進(jìn)行更改時(shí),執(zhí)行該行代碼
 }
})
console.log(obj.name) // undefined,因?yàn)閠emp未賦值,且setter未觸發(fā)
obj.name = '李四' // 賦值進(jìn)行setter觸發(fā),將 '李四'賦值給temp
console.log(obj.name) // 李四

defineReactive提供一個閉包環(huán)境,利用閉包特性進(jìn)行周轉(zhuǎn)

var obj = {}
function defineReactive(data, key, val) {
 console.log(data, key, val)
 Object.defineProperty(data, key, {
     get() {
         return val
     },
     set(newValue) {
         // 如果你要設(shè)置的值和原值相等,直接返回
         if(newValue === val) return 
         val = newValue
     }
 })
}
// 進(jìn)行賦值操作
defineReactive(obj, 'name', '張三')
console.log(obj.name) // 張三
obj.name = '李四'
console.log(obj.name) // 李四

引入遞歸偵測對象(observer)處理對象

// 不進(jìn)行遞歸操作的話,如果一個對象有好幾層,那么我們是檢測不到深層變量的
// eg:
var obj =  {
             a: {
                 b: {
                     c: 'i am c'
                 }
             },
             b: 'i am b'
         }

實(shí)現(xiàn)遞歸

定義observer

把一個正常的object轉(zhuǎn)換為每個層級都是響應(yīng)式的

//defineReactive
function defineReactive(data, key, val) {
 console.log(data, key, val)
 if (arguments.length == 2) {
     val = data[key]
 }
 // 子元素要進(jìn)行遞歸
 let childOb = observe(val)
 Object.defineProperty(data, key, {
     get() {
         return val
     },
     set(newValue) {
         // 如果你要設(shè)置的值和原值相等,直接返回
         if(newValue === val) return 
         val = newValue
         // 設(shè)置了新值,也要被observe
         childOb = observe(newValue)
     }
 })
}
// Observer
class Observer {
 constructor(value) {
     // 給實(shí)例添加__ob__屬性,數(shù)值是這次new的實(shí)例
     def(value, '__ob__', this, false)
     this.walk(value)
 }
 // 遍歷
 walk(value) {
     for (let k in value) {
         defineReactive(value, k)
     }
 }
}
// 不可遍歷的方法
function def(obj, key, value, enumerable) {
 Object.defineProperty(obj, key, {
     value,
     enumerable,
     writable: true,
     configurable: true
 })
}
// 創(chuàng)建observe函數(shù)用于輔助
function observe(value) {
 // 只為對象服務(wù)
 if(typeof value !== 'object') return
 // 定義ob
 if(typeof value.__ob__ !== 'undefined'){
     ob = value.__ob__
 } else {
     ob = new Observer(value)
 }
}
// 循環(huán)監(jiān)測
observe(obj) // 此時(shí)obj每個屬性都被掛載了__ob__標(biāo)識,且為響應(yīng)式
obj.a.b.c.e = 10
console.log(obj.a.b.c.e) // 10

// 簡單來說就是通過__ob__這個標(biāo)識來確認(rèn)你這個obj是不是響應(yīng)式的,如果不是就遞歸遍歷
// observe(obj) ==》 是否有__ob__ ==》 沒有就new Observer(obj) ==》逐個響應(yīng)式defineReactive
  • 處理數(shù)組

vue對數(shù)組的方法進(jìn)行了改寫(push、pop、shiftunshift、splice、sortreverse
Array.prototype為原型創(chuàng)建了一個arrayMethods的對象,用es6Object.setPrototypeOf(),強(qiáng)制指向arrayMethods

// 拿到Array.prototype
const arrayPrototype = Array.prototype
// 以Array.prototype為原型創(chuàng)建arrayMethods對象
const arrayMethods = Object.create(arrayPrototype)
const methodsNeedChange = [
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
]
// 遍歷
methodsNeedChange.foreach(methodName => {
    // 備份原來的方法, 因?yàn)閿?shù)組的功能不能被剝奪
    const original = arrayPrototype[methodName]
    // 定義新的方法
    def(arrayMethods, methodName, functio(this){
        // 恢復(fù)原來的功能
        const result = original.apply(this, arguments)
        // 把類數(shù)組對象變?yōu)閿?shù)組
        const args = [...arguments]
        // 把數(shù)組身上的__ob__取出來,__ob__已經(jīng)被添加了,因?yàn)閿?shù)組不是最高層,在遍歷obj對象第一層的時(shí)候,已經(jīng)添加了__ob__屬性
        const ob = this.__ob__
        //push, unshift, splice可以插入新項(xiàng),因此也要把插入的新項(xiàng)也變?yōu)閛bserve
        let inserted = []
        switch(methodName){
            case 'push':
            case 'unshift':
                inserted = arguments;
                break;
            case 'splice':
                // splice格式是splice(下標(biāo)、數(shù)量、插入的新項(xiàng))
                inserted = args.slice(2);
                break;
        }
        // 判斷有沒有要插入的新項(xiàng), 如果有讓新項(xiàng)也變?yōu)轫憫?yīng)的    
        if (inserted) {
            ob.observeArray(inserted)
        }
        return result
    }, false)
})
// 在observer 中處理數(shù)組
// Observer
class Observer {
    constructor(value) {
        // 給實(shí)例添加__ob__屬性,數(shù)值是這次new的實(shí)例
        def(value, '__ob__', this, false)
        // 如果是數(shù)組,需要把數(shù)組的原型,指向arrayMethods
        if(Array.isArray(value)) {
            Object.setPrototypeOf(value, arrayMethods)
            // 讓數(shù)組可以響應(yīng)式、既observe
            this.observeArray(value)
        } else {
            this.walk(value)
        }
    }
    // 遍歷
    walk(value) {
        for (let k in value) {
            defineReactive(value, k)
        }
    }
    // 數(shù)組的特殊遍歷
    observeArray(arr) {
        for(let i = 0, l = arr.length; i < l; i++) {
            // 逐項(xiàng)進(jìn)行observe
            observe(arr[i])
        }
    }
}
  • 依賴收集

    • 需要用到數(shù)據(jù)的地方,稱為依賴
    • vue1.X, 細(xì)粒度依賴、用到數(shù)據(jù)的DOM依賴
    • vue2.X, 中等粒度依賴、用到數(shù)據(jù)的組件是依賴
    • 在getter收集依賴、在setter觸發(fā)依賴
    • dep類

      • 把依賴收集的代碼封裝,專門用來管理依賴,每個Observer的實(shí)例,成員中都有一個Dep的實(shí)例
      • dep使用發(fā)布訂閱模式、當(dāng)數(shù)據(jù)發(fā)生變化時(shí),會循環(huán)依賴列表,把所有的watcher都通知一遍
    • watcher類

      • 中介、數(shù)據(jù)發(fā)生變化時(shí)通過Watcher進(jìn)行中轉(zhuǎn),通知組件
      • 依賴就是watcher、只有watcher觸發(fā)的getter才會收集依賴、哪個watcher觸發(fā)了getter、就把哪個watcher收集到dep中
    • whatcher把自己設(shè)置到一個全局指定的位置,然后讀取數(shù)據(jù),由于讀取了數(shù)據(jù),會觸發(fā)這個數(shù)據(jù)的getter。
    • 在getter中就能得到當(dāng)前正在讀取數(shù)據(jù)的watcher,并把這個watcher收集到dep中
  • dep類
class Dep{
    constructor(){
        // dep類的構(gòu)造器
        // 用數(shù)組存儲自己訂閱者, 存儲的是watcher的實(shí)例
        this.subs = []
    }
    // 添加訂閱
    addSub(){
        this.subs.push(sub)
    }
    // 添加依賴
    depend() {
        // Dep.target是我們自己指定的全局的位置,主要是全局唯一,沒歧義
        if(Dep.target){
            this.addSub(Dep.target)
        }
    }
    // 通知更新
    notify() {
        // 淺克隆一份
        const subs = this.subs.slice()
        // 遍歷
        for ()let i = 0, l = subs.length; i &lt; 1; i++){
            subs[i].update()
        }
    }
}
  • watcher類
class Watcher{
    constructor(target, expression, callback){
        // watcher類的構(gòu)造器
        this.target = target
        // parsePath是一個高階函數(shù)、用于拿到obj最底層的值
        // getter被賦值的為函數(shù)
        this.getter = parsePath(expression)
        this.callback = callback
        this.value = this.get()  
    }
    update() {
        this.run()
    }
    get() {
        //  進(jìn)入依賴收集階段,讓全局的Dep.target設(shè)置為watcher本身,那么就是進(jìn)入依賴收集階段
        Dep.target = this

        const obj = this.traget
        // 取出最底層的值
        try {
            this.getter(obj)
        } finally {
            Dep.target = null
        }
        
        return value
    }
    run() {
        this.getAndInvoke(this.callback)
    }
    getAndInvoke(cb) {
        const value = this.get()
        if (value !== this.value || typeof value == 'object') {
            const oldValue = this.value
            this.value = value
            cb.call(this.target, value, oldValue)
        }
    }
}
// parsePath函數(shù)
function parsePath(str) {
    var segments = str.split('.')
    
    // 返回一個函數(shù)
    return (obj) =&gt; {
        for (let i = 0; i &lt; segments.length; i++) {
            obj = obj[segments[i]]
        }
        return obj
    }
} 
var fb = parsePath('a.b.c.d.e')
var v =fb({a:{b:{c:{d:{e:12}}}}})
console.log(v) // 12
//

dep和watcher實(shí)例化的位置

dep: defineReactive的閉包中(創(chuàng)建響應(yīng)式中、閉包內(nèi)可以得到)、observer構(gòu)造器內(nèi)

//defineReactive
function defineReactive(data, key, val) {
const dep = new Dep()
console.log(data, key, val)
if (arguments.length == 2) {
    val = data[key]
}
// 子元素要進(jìn)行遞歸
let childOb = observe(val)
Object.defineProperty(data, key, {
    get() {
        // 如果處于依賴收集階段
        if (Dep.target) {
            dep.depend()
            if (childOb) {
                childOb.dep.depend()
            }
        }
        return val
    },
    set(newValue) {
        // 如果你要設(shè)置的值和原值相等,直接返回
        if(newValue === val) return 
        val = newValue
        // 設(shè)置了新值,也要被observe
        childOb = observe(newValue)
        // 發(fā)布訂閱模式,通知dep
    }
})
}
// Observer
class Observer {
constructor(value) {
    // 每一個observer實(shí)例身上,都有一個dep
    this.dep = new Dep()
    // 給實(shí)例添加__ob__屬性,數(shù)值是這次new的實(shí)例
    def(value, '__ob__', this, false)
    // 如果是數(shù)組,需要把數(shù)組的原型,指向arrayMethods
    if(Array.isArray(value)) {
        Object.setPrototypeOf(value, arrayMethods)
        // 讓數(shù)組可以響應(yīng)式、既observe
        this.observeArray(value)
    } else {
        this.walk(value)
    }
}
// 遍歷
walk(value) {
    for (let k in value) {
        defineReactive(value, k)
    }
}
// 數(shù)組的特殊遍歷
observeArray(arr) {
    for(let i = 0, l = arr.length; i < l; i++) {
        // 逐項(xiàng)進(jìn)行observe
        observe(arr[i])
    }
}
}

methodsNeedChange.foreach(methodName => {
// 備份原來的方法, 因?yàn)閿?shù)組的功能不能被剝奪
const original = arrayPrototype[methodName]
// 定義新的方法
def(arrayMethods, methodName, functio(this){
    // 恢復(fù)原來的功能
    const result = original.apply(this, arguments)
    // 把類數(shù)組對象變?yōu)閿?shù)組
    const args = [...arguments]
    // 把數(shù)組身上的__ob__取出來,__ob__已經(jīng)被添加了,因?yàn)閿?shù)組不是最高層,在遍歷obj對象第一層的時(shí)候,已經(jīng)添加了__ob__屬性
    const ob = this.__ob__
    //push, unshift, splice可以插入新項(xiàng),因此也要把插入的新項(xiàng)也變?yōu)閛bserve
    let inserted = []
    switch(methodName){
        case 'push':
        case 'unshift':
            inserted = arguments;
            break;
        case 'splice':
            // splice格式是splice(下標(biāo)、數(shù)量、插入的新項(xiàng))
            inserted = args.slice(2);
            break;
    }
    
    // 判斷有沒有要插入的新項(xiàng), 如果有讓新項(xiàng)也變?yōu)轫憫?yīng)的    
    if (inserted) {
        ob.observeArray(inserted)
    }
    
    ob.dep.notify()
    
    return result
}, false)
})

dep類和watcher類 沒太懂,后續(xù)研究后詳細(xì)說明下,更多關(guān)于vue修改數(shù)據(jù)視圖更新的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue 解決異步數(shù)據(jù)更新問題

    vue 解決異步數(shù)據(jù)更新問題

    今天小編就為大家分享一篇vue 解決異步數(shù)據(jù)更新問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-10-10
  • 一文詳細(xì)了解Vue?3.0中的onMounted和onUnmounted鉤子函數(shù)

    一文詳細(xì)了解Vue?3.0中的onMounted和onUnmounted鉤子函數(shù)

    Vue3.0引入了新的組件生命周期鉤子函數(shù)onMounted和onUnmounted,分別用于組件掛載后和卸載前的操作,這些鉤子函數(shù)為開發(fā)者提供了更多靈活性,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • 在 Linux/Unix 中不重啟 Vim 而重新加載 .vimrc 文件的流程

    在 Linux/Unix 中不重啟 Vim 而重新加載 .vimrc 文件的流程

    這篇文章主要介紹了在 Linux/Unix 中不重啟 Vim 而重新加載 .vimrc 文件的流程,需要的朋友可以參考下
    2018-03-03
  • uniapp中app與webview的通訊代碼示例

    uniapp中app與webview的通訊代碼示例

    這篇文章主要給大家介紹了關(guān)于uniapp中app與webview通訊的相關(guān)資料,這里的通信主要是打包APP端和web-view內(nèi)嵌網(wǎng)頁的雙向通信,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • vue組件傳遞對象中實(shí)現(xiàn)單向綁定的示例

    vue組件傳遞對象中實(shí)現(xiàn)單向綁定的示例

    下面小編就為大家分享一篇vue組件傳遞對象中實(shí)現(xiàn)單向綁定的示例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • 聊聊Vue.js的template編譯的問題

    聊聊Vue.js的template編譯的問題

    這篇文章主要介紹了聊聊Vue.js的template編譯的問題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • vue.nextTick()與setTimeout的區(qū)別及說明

    vue.nextTick()與setTimeout的區(qū)別及說明

    這篇文章主要介紹了vue.nextTick()與setTimeout的區(qū)別及說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue3.2中的vuex使用詳解

    vue3.2中的vuex使用詳解

    這篇文章主要介紹了vue3.2中的vuex使用詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Vue3+Element?Plus的項(xiàng)目搭建過程詳解

    Vue3+Element?Plus的項(xiàng)目搭建過程詳解

    這篇文章主要為大家介紹了Vue3+Element?Plus的項(xiàng)目搭建過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • vue如何使用 jsplumb 生成流程圖

    vue如何使用 jsplumb 生成流程圖

    文章介紹了如何在Vue項(xiàng)目中使用jsPlumb庫來生成流程圖,文章提供了兩個簡單的示例代碼,展示了如何定義流程圖的數(shù)據(jù)字段和如何配置連線樣式,最后,文章鼓勵讀者繼續(xù)探索更多關(guān)于vue和jsPlumb的使用技巧,感興趣的朋友一起看看吧
    2025-02-02

最新評論