v-model原理及視圖更新

vue實現(xiàn)過程

1床玻、new Vue() ?先執(zhí)?初始化,對data執(zhí)?響應(yīng)化處理,這個過程發(fā)?在Observer中

  1. 同時對模板執(zhí)?編譯娘汞,找到其中動態(tài)綁定的數(shù)據(jù),從data中獲取并初始化視圖夕玩,這個過程發(fā)?在
    Compile中
  2. 同時定義?個更新函數(shù)和Watcher你弦,將來對應(yīng)數(shù)據(jù)變化時Watcher會調(diào)?更新函數(shù)
  3. 由于data的某個key在?個視圖中可能出現(xiàn)多次,所以每個key都需要?個管家Dep來管理多個
    Watcher
  4. 將來data中數(shù)據(jù)?旦發(fā)?變化燎孟,會?先找到對應(yīng)的Dep禽作,通知所有Watcher執(zhí)?更新函數(shù)


    image.png
  • Vue:框架構(gòu)造函數(shù)
  • Observer:執(zhí)?數(shù)據(jù)響應(yīng)化(分辨數(shù)據(jù)是對象還是數(shù)組)
  • Compile:編譯模板,初始化視圖揩页,收集依賴(更新函數(shù)旷偿、watcher創(chuàng)建)
  • Watcher:執(zhí)?更新函數(shù)(更新dom)
  • Dep:管理多個Watcher,批量更新

依賴收集

視圖中會?到data中某key爆侣,這稱為依賴萍程。同?個key可能出現(xiàn)多次,每次都需要收集出來??個
Watcher來維護它們兔仰,此過程稱為依賴收集茫负。
多個Watcher需要?個Dep來管理,需要更新時由Dep統(tǒng)?通知斋陪。


image.png

實現(xiàn)思路

  1. defineReactive時為每?個key創(chuàng)建?個Dep實例
  2. 初始化視圖時讀取某個key朽褪,例如name1,創(chuàng)建?個watcher1
  3. 由于觸發(fā)name1的getter?法无虚,便將watcher1添加到name1對應(yīng)的Dep中
  4. 當(dāng)name1更新缔赠,setter觸發(fā)時,便可通過對應(yīng)Dep通知其管理所有Watcher更新

實現(xiàn)代碼

//從頭實現(xiàn)vue的雙向綁定原理 以及視圖更新機制
class Vue {
    constructor(option) {
        //option代表vue的所有對象
        this.$options = option
        this.$data = option.data
        //劫持data中的數(shù)據(jù)
        observe(this.$data)
    }
}
//劫持所有屬性
class Observe {
    constructor(value) {
        this.value = value
        this.walk(this.value)
    }
    //walk遍歷對象中的每一個key,每一個屬性都調(diào)用一次defineReactive 來實現(xiàn)劫持
    walk(obj) {
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key])
        })
    }
}
//定義一個管家 管家中存放自己需要管理的watcher
class Dep {
    constructor() {
        //存放watcher
        this.deps = []
    }
    //這里將watch添加到管家中
    addDep(dep) {
        this.deps.push(dep)
    }
    //觸發(fā)自己管理的每一個watch的更新方法 
    notify() {
        this.deps.forEach(dep => dep.update())
    }
}

class Watcher {
    constructor(vm, key, updateFn) {
        this.vm = vm
        this.updateFn = updateFn
        this.key = key
        //解析插值表達式的時候?qū)嵗疻atch友题,因為要用到值嗤堰,會觸發(fā)getter方法 這時候給Dep.target賦值
        Dep.target = this
        this.vm[this.key]  //調(diào)用data中的值,觸發(fā)definePrototy的getter方法度宦,開始收集依賴
        Dep.target = null  //每次將watcher添加到dep中后清空 不影響下次添加
    }
    //更新視圖的方法
    update() {
        //傳入一個根實例踢匣,一個當(dāng)前的值
        this.updateFn.call(this.vm, this.vm[key])
    }
}

//初始化視圖方法
class Compile {
    constructor(el, vm) {
        this.$vm = vm
        this.$el = document.querySelctor(el)
        //如果拿到節(jié)點,就將節(jié)點編譯e
        if (this.$el) {
            this.complie(this.$el)
        }
    }
    complie(value) {
        const childNodes = value.childNodes;
        Array.from(childNodes).forEach(node => {
            if (this.isElement(node)) {
                console.log("編譯元素" + node.nodeName);
            } else if (this.isInterpolation(node)) {
                //編譯插值文本的時候創(chuàng)建watcher 并更新dom
                console.log("編譯插值?本" + node.textContent);
                this.compileText(node);
            }
            //遞歸編譯
            if (node.childNodes && node.childNodes.length > 0) {
                this.compile(node);
            }
        });

    }
    isElement(node) {
        return node.nodeType == 1;
    }
    isInterpolation(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    compileText(node) {
        // console.log(RegExp.$1);
        this.update(node, RegExp.$1, 'text')
    }
    update(node, exp, dir) {
        const fn = this[dir + 'Updater']
        fn && fn(node, this.$vm[exp])  //給節(jié)點賦值
        new Watcher(this.$vm, exp, function (val) {  //添加監(jiān)聽 觸發(fā)的時候調(diào)用此方法更新節(jié)點
            fn && fn(node, val)
        })
    }
    textUpdater(node, val) {
        node.textContent = val;
    }
    htmlUpdater(node, val) {
        node.innerHTML = val
    }
}

function observe(obj) {
    if (typeof obj != 'object' || obj === null) return
    new Observe(obj)
}

function defineReactive(obj, key, val) {
    //解決嵌套對象問題 采用遞歸方法
    observe(val)

    //實例化管家戈抄,為管家添加多個watcher
    const dep = new dep()

    Object.defineProperty(obj, key, {
        get() {
            //收集依賴 Dep.target在Watcher創(chuàng)建的時候被賦值 其實指向了watch的實例 watch的實例中有update方法离唬,
            Dep.target && dep.addDep(Dep.target)
            return val
        },
        set(newVal) {
            if (newVal != val) {
                //將管家中多個watch更新
                dep.notify()
            }
        }
    })
}
function compileElement(node) {
    let nodeAttrs = node.attributes;
    Array.from(nodeAttrs).forEach(attr => {
        let attrName = attr.name;
        let exp = attr.value;
        if (this.isDirective(attrName)) {
            let dir = attrName.substring(2);
            this[dir] && this[dir](node, exp);
        }
    });
}



?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市划鸽,隨后出現(xiàn)的幾起案子输莺,更是在濱河造成了極大的恐慌戚哎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫂用,死亡現(xiàn)場離奇詭異型凳,居然都是意外死亡,警方通過查閱死者的電腦和手機嘱函,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門甘畅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人往弓,你說我怎么就攤上這事疏唾。” “怎么了亮航?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵荸实,是天一觀的道長。 經(jīng)常有香客問我缴淋,道長准给,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任重抖,我火速辦了婚禮露氮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钟沛。我一直安慰自己畔规,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布恨统。 她就那樣靜靜地躺著叁扫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畜埋。 梳的紋絲不亂的頭發(fā)上莫绣,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音悠鞍,去河邊找鬼对室。 笑死,一個胖子當(dāng)著我的面吹牛咖祭,可吹牛的內(nèi)容都是我干的掩宜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼么翰,長吁一口氣:“原來是場噩夢啊……” “哼牺汤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浩嫌,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤慧瘤,失蹤者是張志新(化名)和其女友劉穎戴已,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锅减,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年伐坏,在試婚紗的時候發(fā)現(xiàn)自己被綠了怔匣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡桦沉,死狀恐怖每瞒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纯露,我是刑警寧澤剿骨,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站埠褪,受9級特大地震影響浓利,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钞速,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一贷掖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渴语,春花似錦苹威、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至调违,卻和暖如春窟哺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翰萨。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工脏答, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亩鬼。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓殖告,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雳锋。 傳聞我的和親對象是個殘疾皇子黄绩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354