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

資訊專欄INFORMATION COLUMN

讀vue的變化偵測(cè)

zhoutk / 2357人閱讀

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

由來

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

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

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

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

    那么這個(gè)依賴,誰來收集存起來。通過Dep類來實(shí)現(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對(duì)象進(jìn)行遍歷設(shè)置其屬性描述對(duì)象

    get的設(shè)置就是為了在數(shù)據(jù)被訪問時(shí),將依賴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這個(gè)方法來自watcher實(shí)例對(duì)象的方法
        }
    }
}
function remove(arr, item) {
    if(arr.length) {
        const index = arr.indexOf(item)
        if(index > -1) {
            return arr.splice(index, 1)
        }
        
    }
}

分析一下

    主要就是對(duì)dep實(shí)例對(duì)象的增刪改查的操作

    window.target 這個(gè)依賴怎么來,就看watcher實(shí)例對(duì)象了

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)做了什么?從下面代碼中可以看出作用就是返回一個(gè)函數(shù),這個(gè)函數(shù)用來讀取value

const bailRE = /[^w.$]/  //
function parsePath(path) {
    if(bailRE.test(path) {
        return         //當(dāng)path路徑中有一個(gè)字符不滿足正則要求就直接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時(shí)會(huì)執(zhí)行this.value,從而執(zhí)行this.get(),所以這時(shí)的window.target是當(dāng)前watcher實(shí)例對(duì)象this;接著執(zhí)行this.getter.call(this.vm, this.vm),觸發(fā)屬性描述對(duì)象的get方法,進(jìn)行dep.depend(),最后將其window.target = undefined

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

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

    依賴被重復(fù)添加

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

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

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

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

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

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

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

const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto) //新建對(duì)象,繼承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實(shí)例對(duì)象 
        if(Array.isArray(value) {
            const augment = hasProto ");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實(shí)例對(duì)象,避免重復(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
        //對(duì)于新增變化的元素頁進(jìn)行observerArray()
        switch (method) {   //因?yàn)檫@幾個(gè)是有參數(shù)的
            case "push":
            case "unshift":       //因?yàn)閜ush和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] }為例

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

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

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

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

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

    對(duì)于this.list[0] = 2,這種通過索引來改變?cè)刂禃r(shí)頁一樣不會(huì)觸發(fā)更新

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

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

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

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

相關(guān)文章

  • vue變化偵測(cè)

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

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

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

    yiliang 評(píng)論0 收藏0
  • Vue偵測(cè)相關(guān)api

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

    zhangfaliang 評(píng)論0 收藏0
  • Vue源碼分析之Observer

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

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

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

    malakashi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<