成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

讀vue的變化偵測

Freeman / 923人閱讀

摘要:由來最近在看深入淺出,第一篇變化偵測,想把自己的理解總結(jié)一下。的變化偵測總結(jié)一下我看了后的理解將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。

由來

最近在看“深入淺出vuejs”,第一篇變化偵測,想把自己的理解總結(jié)一下。

Object的變化偵測 總結(jié)一下我看了后的理解

將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。通過Observer類來實現(xiàn)

依賴是什么?就是這個數(shù)據(jù)在哪里用到了,相當(dāng)于this當(dāng)前的上下文;所以當(dāng)數(shù)據(jù)變化時,我們可以通知他,觸發(fā)update,從而觸發(fā)渲染

那么這個依賴,誰來收集存起來。通過Dep類來實現(xiàn)

先看Observer
class Observer {
    constructor(value) {
        this.value = value
        if(!Array.isArray(value) {
            this.walk(value)
        }
    }
    walk (obj) {
        const keys = Object.keys(obj)
        for(let i = 0; i < keys.length; i++) {
            definedReactive(obj, keys[i], obj[keys[i]])
        }
    }
}
function definedReactive(data, key, value) {
    if(typeof val === "object") {
        new Observer(value)
    }
    let dep = new Dep()
    Object.defineProperty(data, key, {
        enumberable: true,
        configurable: true,
        get: function () {
            dep.depend()
            return value
        },
        set: function (newVal) {
            if(value === newVal) {    //這邊最好是value === newVal || (value !== value && newVal !== newVal)
                return 
            }
            value = newVal   //這邊新的newVal如果是引用類型也應(yīng)該進(jìn)行進(jìn)行new Observer()
            dep.notify()
        }
    })
}
很容易看懂

將vue中的data對象進(jìn)行遍歷設(shè)置其屬性描述對象

get的設(shè)置就是為了在數(shù)據(jù)被訪問時,將依賴dep.depend()進(jìn)去,至于做了什么看詳細(xì)看Dep類

set的設(shè)置則是為了判斷新值和舊值是否一樣(注意NaN),若不一樣,則執(zhí)行dep.notify(),通知相應(yīng)依賴進(jìn)行更新變化

Dep類
class Dep {
    constructor () {
        this.subs = []    //存放依賴
    }
    addSub () {
        this.subs.push(sub)
    },
    remove () {
        remove(this.subs, sub) 
    },
    depend () {
        if(window.target) {
            this.addSub(window.target)   //window.target 是this,watcher的上下文
        }
    },
    notify () {
        const subs = this.subs.slice()
        for(let i = 0, l = subs.length; i < l; i ++) {
            subs[i].update()       //update這個方法來自watcher實例對象的方法
        }
    }
}
function remove(arr, item) {
    if(arr.length) {
        const index = arr.indexOf(item)
        if(index > -1) {
            return arr.splice(index, 1)
        }
        
    }
}
分析一下

主要就是對dep實例對象的增刪改查的操作

window.target 這個依賴怎么來,就看watcher實例對象了

Watcher類

初版:

class Watcher {
    constructor (vm, expOrFn, cb) {
        this.vm = vm
        this.getter = parsePath(expOrFn)
        this.cb = cb
        this.value = this.get()
    }
    get() {
        window.target = this
        let value = this.getter.call(this.vm, this.vm)
        window.target = undefined
        return value
    }
    update() {
        const oldValue = this.value
        this.value = this.get()
        this.cb.call(this.vm, this.value, oldValue)
    }
}
分析

怎么觸發(fā)?可以利用

vm.$watch("data.a", function (newValue, oldValue) {
    //執(zhí)行相關(guān)操作
})

parsePath(expOrFn)做了什么?從下面代碼中可以看出作用就是返回一個函數(shù),這個函數(shù)用來讀取value

const bailRE = /[^w.$]/  //
function parsePath(path) {
    if(bailRE.test(path) {
        return         //當(dāng)path路徑中有一個字符不滿足正則要求就直接return
    }
    return function () {
        const arr = path.split(".")
        let data = this
        for(let i = 0, l = arr.length; i < l; i ++) {
            let data = data.arr[i]
        }
        return data
    }
}

new Watcher時會執(zhí)行this.value,從而執(zhí)行this.get(),所以這時的window.target是當(dāng)前watcher實例對象this;接著執(zhí)行this.getter.call(this.vm, this.vm),觸發(fā)屬性描述對象的get方法,進(jìn)行dep.depend(),最后將其window.target = undefined

update的方法是在數(shù)據(jù)改變后觸發(fā),但這邊有個問題就是會重復(fù)添加依賴

上面版本中比較明顯的問題

依賴被重復(fù)添加

只能對已有key進(jìn)行監(jiān)聽

刪除key-value不會被監(jiān)聽

對數(shù)組對象,并沒有添加監(jiān)聽

對于數(shù)據(jù)變化時,并沒有對新數(shù)據(jù)判斷是否需要進(jìn)行Observer

Array的偵測 怎么實現(xiàn)在數(shù)組發(fā)生變化時來觸發(fā)dep.notify(),以及如何收集數(shù)組的依賴

通過push, pop, shift, unshift, splice, sort, reverse這幾個方法的封裝來觸發(fā)dep.notify()

怎么的封裝?分兩種;第一種對于支持_proto_屬性的,直接改寫原型鏈的這些方法;第二種對于不支持的,直接在實例對象上添加改變后的7個方法

const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto) //新建對象,繼承Array的原型鏈
class Observer {
    constructor (value) {
        this.value = value
        this.dep = new Dep()      //在Observer中添加dep屬性為了記錄數(shù)組的依賴
        def(value, "_ob_", this)  //在當(dāng)前value上新增`_ob_`屬性,其值為this,當(dāng)前observer實例對象 
        if(Array.isArray(value) {
            const augment = hasProto ? protoAugment : copyAugment
            augment(value, arrayMethods, arrayKeys)
            this.observerArray(value)  //將數(shù)組內(nèi)元素也進(jìn)行Observer
        }else {
            this.walk(value)
        }
    }
    //新增
    observerArray (items) {
        for(let i = 0, l = items.length; i < l; i ++) {
            observe(items[i])
        }
    }
}
//作用就是為obj,添加key值為val  
function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true
    })
}
function observe(value, asRootData) {
    if(!isObject(value)) {
        return 
    }
    let ob
    //判斷value是否已經(jīng)是Observer實例對象,避免重復(fù)執(zhí)行Observer
    if(hasOwn(value, "_ob_") && value._ob_ instanceof Observer) {
        ob = value._ob_
    } else {
        ob = new Observer(value)
    }
    return ob
}
function definedReactive(data, key, value) {
    let childOb = observe(value)   //修改
    let dep = new Dep()
    Object.defineProperty(data, key, {
        enumberable: true,
        configurable: true,
        get: function () {
            dep.depend()
            if(childOb) {            //新增
                childOb.dep.depend()
            }
            return value
        },
        set: function (newVal) {
            if(value === newVal) {    //這邊最好是value === newVal || (value !== value && newVal !== newVal)
                return 
            }
            value = newVal   //這邊新的newVal如果是引用類型也應(yīng)該進(jìn)行進(jìn)行new Observer()
            dep.notify()
        }
    })
}
//觸發(fā)數(shù)組攔截
;[
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse"
].forEach(function (method) {
    const original = arrayProto[method]
    def(arrayMethods, method, function mutator() {
        const result = original.apply(this, args)
        const ob =  this._ob_   //this就是數(shù)據(jù)value
        let inserted
        //對于新增變化的元素頁進(jìn)行observerArray()
        switch (method) {   //因為這幾個是有參數(shù)的
            case "push":
            case "unshift":       //因為push和unshift都是一樣的取args,所以push不需要加break了
                inserted = args
                break
            case "splice":    //新增變化元素是從索引2開始的
                inserted = args.slice(2)
                break
        }
        ob.dep.notify()  //通知依賴執(zhí)行update
        return result
    })
}
分析,已data = { a: [1, 2, 3] }為例

首先對data對象進(jìn)行Observer,將執(zhí)行this.walk(data)

接著執(zhí)行let childOb = observe(val),發(fā)現(xiàn)value是一個數(shù)組對象,進(jìn)行Observer,主要進(jìn)行是augment(value, arrayMethods, arrayKeys),將7個方法進(jìn)行攔截,接著遍歷內(nèi)部元素是否有引用數(shù)據(jù)類型,有繼續(xù)Observer,最后返回Observer實例對象ob

重點是get方法,當(dāng)數(shù)據(jù)data被訪問時,首先執(zhí)行dep.depend()這里將依賴添加到datadep中;接著因為childObtrue所以執(zhí)行childOb.dep.depend(),這里是將依賴加入到observer實例對象的dep中,為什么,這個dep是給數(shù)組發(fā)生變化時執(zhí)行this._ob_.dep.notify(),這個this就是value對象,因為def(value, "_ob_", this) ,所以可以執(zhí)行dep.notify()

這種數(shù)組變化偵測存在的問題

對于進(jìn)行this.list.length = 0進(jìn)行清空時,不會觸發(fā)它的依賴更新,也就不會觸發(fā)視圖的渲染更新

對于this.list[0] = 2,這種通過索引來改變元素值時頁一樣不會觸發(fā)更新

所以我們盡量避免通過這種方式來改變數(shù)據(jù)

還有vm.$watch,vm.$set,vm.$delete下篇中進(jìn)行整理

掘金地址

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109682.html

相關(guān)文章

  • vue變化偵測

    摘要:由來最近在看深入淺出,第一篇變化偵測,想把自己的理解總結(jié)一下。的變化偵測總結(jié)一下我看了后的理解將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。由來 最近在看深入淺出vuejs,第一篇變化偵測,想把自己的理解總結(jié)一下。 Object的變化偵測 總結(jié)一下我看了后的理解 將數(shù)據(jù)變成可響應(yīng)式的,即將數(shù)據(jù)變成可監(jiān)聽的。通過Observer類來實現(xiàn) 依賴是什么?就是這個數(shù)據(jù)在哪里用到了,相當(dāng)于this當(dāng)...

    zhoutk 評論0 收藏0
  • 深入淺出Vue響應(yīng)式原理

    摘要:總結(jié)最后我們依照下圖參考深入淺出,再來回顧下整個過程在后,會調(diào)用函數(shù)進(jìn)行初始化,也就是過程,在這個過程通過轉(zhuǎn)換成了的形式,來對數(shù)據(jù)追蹤變化,當(dāng)被設(shè)置的對象被讀取的時候會執(zhí)行函數(shù),而在當(dāng)被賦值的時候會執(zhí)行函數(shù)。 前言 Vue 最獨特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 JavaScript 對象。而當(dāng)你修改它們時,視圖會進(jìn)行更新。這使得狀態(tài)管理非常簡單直接,不過理解...

    yiliang 評論0 收藏0
  • Vue偵測相關(guān)api

    vm.$watch 用法: vm.$watch( expOrFn, callback, [options] ),返回值為unwatch是一個函數(shù)用來取消觀察;下面主要理解options中的兩個參數(shù)deep和immediate以及unwatch Vue.prototype.$watch = function (expOrFn, cb, options) { const vm = this ...

    zhangfaliang 評論0 收藏0
  • Vue源碼分析之Observer

    摘要:中的觀察者模式觀察者模式一般包含發(fā)布者和訂閱者兩種角色顧名思義發(fā)布者負(fù)責(zé)發(fā)布消息,訂閱者通過訂閱消息響應(yīng)動作了。中主要有兩種類型的,一種是另外一種是是通過或者中的屬性定義的。結(jié)束好了,基本結(jié)束,如有錯漏,望指正。 碎碎念 四月份真是慵懶無比的一個月份,看著手頭上沒啥事干,只好翻翻代碼啥的,看了一會Vue的源碼,忽而有點感悟,于是便記錄一下。 Vue中的觀察者模式 觀察者模式一般包含發(fā)布...

    CoderBear 評論0 收藏0
  • Vue面試題精選:Vue原理以及雙向數(shù)據(jù)綁定實戰(zhàn)過程

    摘要:雙向數(shù)據(jù)綁定指的是,將對象屬性變化與視圖的變化相互綁定。數(shù)據(jù)雙向綁定已經(jīng)了解到是通過數(shù)據(jù)劫持的方式來做數(shù)據(jù)綁定的,其中最核心的方法便是通過來實現(xiàn)對屬性的劫持,達(dá)到監(jiān)聽數(shù)據(jù)變動的目的。和允許觀察數(shù)據(jù)的更改并觸發(fā)更新。 1 MVVM 雙向數(shù)據(jù)綁定指的是,將對象屬性變化與視圖的變化相互綁定。換句話說,如果有一個擁有name屬性的user對象,與元素的內(nèi)容綁定,當(dāng)給user.name賦予一個新...

    malakashi 評論0 收藏0

發(fā)表評論

0條評論

Freeman

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<