久久精品中文字幕免费_91香蕉国产亚洲一区二区三区_国产精品巨作无遮拦_亚洲人成电影

    <center id="oy65s"><ol id="oy65s"></ol></center>

  • <menu id="oy65s"></menu>
    當前位置:首頁 > 足球資訊 > 正文內(nèi)容

    VUE 響應(yīng)式原理源碼:帶你一步精通 VUE | 原力計劃

    杏彩體育2年前 (2023-01-28)足球資訊46

    作者 | 愛編程的小和尚

    責編 | 王曉曼

    出品 | CSDN博客

    學(xué)過 VUE 如果不了解響應(yīng)式的原理,怎么能說自己熟練使用 VUE,要是沒有寫過一個簡易版的 VUE 怎么能說自己精通 VUE,這篇文章通過300多行代碼,帶你寫一個簡易版的 VUE,主要實現(xiàn) VUE 數(shù)據(jù)響應(yīng)式 (數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者)、數(shù)組的變異方法、編譯指令,數(shù)據(jù)的雙向綁定的功能。

    本文需要有一定 VUE 基礎(chǔ),并不適合新手學(xué)習。

    文章較長,且有些難度,建議大家,找一個安靜的環(huán)境,并在看之前沐浴更衣,保持編程的神圣感。下面是實現(xiàn)的簡易版VUE 的源碼地址,一定要先下載下來!因為文章中的并非全部的代碼。

    Github源碼地址:

    https://github.com/young-monk/myVUE.git

    前言

    在開始學(xué)習之前,我們先來了解一下什么是 MVVM ,什么是數(shù)據(jù)響應(yīng)式。

    我們都知道 VUE 是一個典型的 MVVM 思想,由數(shù)據(jù)驅(qū)動視圖。

    那么什么是 MVVM 思想呢?

    MVVM是Model-View-ViewModel,是把一個系統(tǒng)分為了模型( model )、視圖( view )和 view-model 三個部分。

    VUE在 MVVM 思想下,view 和model 之間沒有直接的聯(lián)系,但是 view 和 view-model 、model和 view-model之間時交互的,當 view 視圖進行 dom 操作等使數(shù)據(jù)發(fā)生變化時,可以通過 view-model 同步到 model 中,同樣的 model 數(shù)據(jù)變化也會同步到 view 中。

    那么實現(xiàn)數(shù)據(jù)響應(yīng)式都有什么方法呢?1、發(fā)布者-訂閱者模式:當一個對象(發(fā)布者)狀態(tài)發(fā)生改變時,所有依賴它的對象(訂閱者)都會得到通知。通俗點來講,發(fā)布者就相當于報紙,而訂閱者相當于讀報紙的人。2、臟值檢查:通過存儲舊的數(shù)據(jù),和當前新的數(shù)據(jù)進行對比,觀察是否有變更,來決定是否更新視圖。angular.js 就是通過臟值檢查的方式。最簡單的實現(xiàn)方式就是通過 setInterval 定時輪詢檢測數(shù)據(jù)變動,但這樣無疑會增加性能,所以, angular 只有在指定的事件觸發(fā)時進入臟值檢測。3、數(shù)據(jù)劫持:通過 Object.defineProperty 來劫持各個屬性的 setter,getter,在數(shù)據(jù)變動時觸發(fā)相應(yīng)的方法。VUE是如何實現(xiàn)數(shù)據(jù)響應(yīng)式的呢?

    VUE.js 則是通過數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式。

    當執(zhí)行 new VUE 時,VUE 就進入了初始化階段,VUE會對指令進行解析(初始化視圖,增加訂閱者,綁定更新函數(shù)),同時通過 Obserber會遍歷數(shù)據(jù)并通過 Object.defineProperty 的 getter 和 setter 實現(xiàn)對的監(jiān)聽, 當數(shù)據(jù)發(fā)生變化的時候,Observer 中的 setter 方法被觸發(fā),setter 會立即調(diào)用Dep.notify, Dep 開始遍歷所有的訂閱者,并調(diào)用訂閱者的 update 方法,訂閱者收到通知后對視圖進行相應(yīng)的更新。

    我來依次介紹一下圖中的重要的名詞:1、Observer:數(shù)據(jù)監(jiān)聽器,能夠?qū)?shù)據(jù)對象的所有屬性進行監(jiān)聽,如有變動可拿到最新值并通知訂閱者,內(nèi)部采用 Object.defineProperty 的 getter 和 setter 來實現(xiàn)2、Compile:指令解析器,它的作用對每個元素節(jié)點的指令進行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)3、Dep:訂閱者收集器或者叫消息訂閱器都可以,它在內(nèi)部維護了一個數(shù)組,用來收集訂閱者,當數(shù)據(jù)改變觸發(fā) notify 函數(shù),再調(diào)用訂閱者的 update 方法4、Watcher:訂閱者,它是連接 Observer 和 Compile 的橋梁,收到消息訂閱器的通知,更新視圖5、Updater:視圖更新所以我們想要實現(xiàn)一個 VUE 響應(yīng)式,需要完成數(shù)據(jù)劫持、依賴收集、 發(fā)布者訂閱者模式。下面我來介紹我模仿源碼實現(xiàn)的功能:

    1、數(shù)據(jù)的響應(yīng)式、雙向綁定,能夠?qū)?shù)據(jù)對象的所有屬性進行監(jiān)聽,如有變動可拿到最新值并通知訂閱者

    2、解析 VUE 常用的指令 v-html,v-text,v-bind,v-on,v-model,包括( @ 和 : )

    3、數(shù)組變異方法的處理

    4、在 VUE 中使用 this 訪問或改變 data 中的數(shù)據(jù)

    我們想要完成以上的功能,需要實現(xiàn)如下類和方法:

    1、實現(xiàn) Observe r類:對所有的數(shù)據(jù)進行監(jiān)聽

    2、實現(xiàn) array 工具方法:對變異方法的處理

    3、實現(xiàn) Dep 類:維護訂閱者

    4、實現(xiàn) Watcher 類:接收 Dep 的更新通知,用于更新視圖

    5、實現(xiàn) Compile 類:用于對指令進行解析

    6、實現(xiàn)一個 CompileUtils 工具方法,實現(xiàn)通過指令更新視圖、綁定更新函數(shù)Watcher

    7、實現(xiàn) this.data 代理:實現(xiàn)對 this. data 代理:實現(xiàn)對 this.data 代理:實現(xiàn)對 this.data 代理,可以直接在 VUE 中使用 this 獲取當前數(shù)據(jù)

    我是使用了webpack作為構(gòu)建工具來協(xié)同開發(fā)的,所以在我實現(xiàn)的VUE響應(yīng)式中會用到ES6模塊化,webpack的相關(guān)知識。

    實現(xiàn) Observer 類

    我們都知道要用 Obeject.defineProperty 來監(jiān)聽屬性的數(shù)據(jù)變化,我們需要對 Observer 的數(shù)據(jù)對象進行遞歸遍歷,包括子屬性對象的屬性,都加上 setter 和 getter ,這樣的話,當給這個對象的某個值賦值,就會觸發(fā) setter,那么就能監(jiān)聽到了數(shù)據(jù)變化。當然我們在新增加數(shù)據(jù)的時候,也要對新的數(shù)據(jù)對象進行遞歸遍歷,加上 setter 和 getter 。

    但我們要注意數(shù)組,在處理數(shù)組時并不是把數(shù)組中的每一個元素都加上 setter 和 getter ,我們試想一下,一個從后端返回的數(shù)組數(shù)據(jù)是非常龐大的,如果為每個屬性都加上 setter 和 getter ,性能消耗是十分巨大的。我們想要得到的效果和所消耗的性能不成正比,所以在數(shù)組方面,我們通過對數(shù)組的7 個變異方法來實現(xiàn)數(shù)據(jù)的響應(yīng)式。只有通過數(shù)組變異方法來修改和刪除數(shù)組時才會重新渲染頁面。

    那么監(jiān)聽到變化之后是如何通知訂閱者來更新視圖的呢?我們需要實現(xiàn)一個Dep(消息訂閱器),其中有一個 notify 方法,是通知訂閱者數(shù)據(jù)發(fā)生了變化,再讓訂閱者來更新視圖。

    我們怎么添加訂閱者呢?我們可以通過 new Dep,通過 Dep 中的addSaubs 方法來添加訂閱者。我們來看一下具體代碼。

    我們首先需要聲明一個 Observer 類,在創(chuàng)建類的時候,我們需要創(chuàng)建一個消息訂閱器,判斷一下是否是數(shù)組,如果是數(shù)組,我們便改造數(shù)組,如果是對象,我們便需要為對象的每一個屬性都加入 setter 和 getter 。

    import { arrayMethods } from ./array //數(shù)組變異方法處理

    class Observer {

    constructor(data) {

    //用于對數(shù)組進行處理,存放數(shù)組的觀察者watcher

    this.dep = new Dep

    if (Array.isArray(data)) {

    //如果是數(shù)組,使用數(shù)組的變異方法

    data.__proto__ = arrayMethods

    //把數(shù)組數(shù)據(jù)添加 __ob__ 一個Observer,當使用數(shù)組變異方法時,可以更新視圖

    data.__ob__ = this

    //給數(shù)組的每一項添加數(shù)據(jù)劫持(setter/getter處理)

    this.observerArray(data)

    } else {

    //非數(shù)組數(shù)據(jù)添加數(shù)據(jù)劫持(setter/getter處理)

    this.walk(data)

    }

    }

    }

    在上面,我們給 data 的__proto__原型鏈重新賦值,我們來看一下 arrayMethods 是什么,arrayMethods 是 array.js 文件中,拋出的一個新的 Array 原型:

    // 獲取Array的原型鏈

    const arrayProto = Array.prototype;

    // 重新創(chuàng)建一個含有對應(yīng)原型的對象,在下面稱為新Array

    const arrayMethods = Object.create(arrayProto);

    // 處理7個數(shù)組變異方法

    [push, pop, shift, unshift, reverse, sort, splice].forEach(ele => {

    //修改新Array的對應(yīng)的方法

    arrayMethods[ele] = function {

    // 執(zhí)行數(shù)組的原生方法,完成其需要完成的內(nèi)容

    arrayProto[ele].call(this, ...arguments)

    // 獲取Observer對象

    const ob = this.__ob__

    // 更新視圖

    ob.dep.notify

    }

    })

    export {

    arrayMethods

    }

    此時呢,我們就擁有了數(shù)組的變異方法,我們還需要通過 observerArray 方法為數(shù)組的每一項添加 getter 和setter ,注意,此時的每一項只是最外面的一層,并非遞歸遍歷。

    //循環(huán)遍歷數(shù)組,為數(shù)組每一項設(shè)置setter/getter

    observerArray(items) {

    for (let i = 0; i < items.length; i++) {

    this.observer(items[i])

    }

    }

    如果是一個對象的話,我們就要對對象 的每一個屬性遞歸遍歷,通過 walk 方法:

    walk(data) {

    //數(shù)據(jù)劫持

    if (data && typeof data === "object") {

    for (const key in data) {

    //綁定setter和getter

    this.defineReactive(data, key, data[key])

    }

    }

    }

    在上面的調(diào)用了 defineReactive ,我們來看看這個方法是干什么的?這個方法就是設(shè)置數(shù)據(jù)劫持的,每一行都有注釋。

    //數(shù)據(jù)劫持,設(shè)置 setter/getteer

    defineReactive(data, key, value) {

    //如果是數(shù)組的話,需要接受返回的Observer對象

    let arrayOb = this.observer(value)

    //創(chuàng)建訂閱者/收集依賴

    const dep = new Dep

    //setter和getter處理

    Object.defineProperty(data, key, {

    //可枚舉的

    enumerable: true,

    //可修改的

    configurable: false,

    get {

    //當 Dep 有 watcher 時, 添加 watcher

    Dep.target && dep.addSubs(Dep.target)

    //如果是數(shù)組,則添加上數(shù)組的觀察者

    Dep.target && arrayOb && arrayOb.dep.addSubs(Dep.target)

    return value

    },

    set: (newVal) => {

    //新舊數(shù)據(jù)不相等時更改

    if (value !== newVal) {

    //為新設(shè)置的數(shù)據(jù)添加setter/getter

    arrayOb = this.observer(newVal);

    value = newVal

    //通知 dep 數(shù)據(jù)發(fā)送了變化

    dep.notify

    }

    }

    })

    }

    }

    我們需要注意的是,在上面的圖解中,在 Observer 中,如果數(shù)據(jù)發(fā)生變化,會通知消息訂閱器,那么在何時綁定消息訂閱器呢?就是在設(shè)置 setter 和 getter 的時候,創(chuàng)建一個 Dep,并為 Dep添加訂閱者,Dep.target&& dep.addSubs(Dep.target),通過調(diào)用 dep 的 addSubs 方法添加訂閱者。

    實現(xiàn) Dep

    Dep 是消息訂閱器,它的作用就是維護一個訂閱者數(shù)組,當數(shù)據(jù)發(fā)送變化是,通知對應(yīng)的訂閱者,Dep中有一個 notify 方法,作用就是通知訂閱者,數(shù)據(jù)發(fā)送了變化:

    // 訂閱者收集器

    export default class Dep {

    constructor {

    //管理的watcher的數(shù)組

    this.subs =

    }

    addSubs(watcher) {

    //添加watcher

    this.subs.push(watcher)

    }

    notify {

    //通知watcher更新dom

    this.subs.forEach(w => w.update)

    }

    }

    實現(xiàn) watcher

    Watcher 就是訂閱者, watcher 是 Observer 和 Compile 之間通信的橋梁,當數(shù)據(jù)改變時,接收到 Dep 的通知(Dep 的notify()方法),來調(diào)用自己的update方法,觸發(fā) Compile 中綁定的回調(diào),達到更新視圖的目的。

    import Dep from ./dep

    import { complieUtils } from ./utils

    export default class Watcher {

    constructor(vm, expr, cb) {

    //當前的vue實例

    this.vm = vm;

    //表達式

    this.expr = expr;

    //回調(diào)函數(shù),更新dom

    this.cb = cb

    //獲取舊的數(shù)據(jù),此時獲取舊值的時候,Dep.target會綁定上當前的this

    this.oldVal = this.getOldVal

    }

    getOldVal {

    //將當前的watcher綁定起來

    Dep.target = this

    //獲取舊數(shù)據(jù)

    const oldVal = complieUtils.getValue(this.expr, this.vm)

    //綁定完成后,將綁定的置空,防止多次綁定

    Dep.target =

    return oldVal

    }

    update {

    //更新函數(shù)

    const newVal = complieUtils.getValue(this.expr, this.vm)

    if (newVal !== this.oldVal || Array.isArray(newVal)) {

    //條用更新在compile中創(chuàng)建watcher時傳入的回調(diào)函數(shù)

    this.cb(newVal)

    }

    }

    }

    上面中用到了 ComplieUtils 中的 getValue 方法,會在下面講,主要作用是獲取到指定表達式的值。

    我們把整個流程分成兩條路線的話:

    newVUE ==> Observer數(shù)據(jù)劫持 ==> 綁定Dep ==> 通知watcher ==> 更新視圖newVUE ==> Compile解析模板指令 ==> 初始化視圖 和 綁定watcher

    此時,我們第一條線的內(nèi)容已經(jīng)實現(xiàn)了,我們再來實現(xiàn)一下第二條線。

    實現(xiàn) Compile

    Compile 主要做的事情是解析模板指令,將模板中的變量替換成數(shù)據(jù),初始化渲染頁面視圖。同時也要綁定更新函數(shù),添加訂閱者。

    因為在解析的過程中,會多次的操作 dom,為提高性能和效率,會先將 VUE 實例根節(jié)點的 el 轉(zhuǎn)換成文檔碎片 fragment 進行解析編譯操作,解析完成,再將 fragment 添加回原來的真實 dom 節(jié)點中。

    class Complie {

    constructor(el, vm) {

    this.el = this.isNodeElement(el) ? el : document.querySelector(el);

    this.vm = vm;

    // 1、將所有的dom對象放到fragement文檔碎片中,防止重復(fù)操作dom,消耗性能

    const fragments = this.nodeTofragments(this.el)

    // 2、編譯模板

    this.complie(fragments)

    // 3、追加子元素到根元素

    this.el.appendChild(fragments)

    }

    }

    我們可以看到,Complie 中主要進行了三步,第一步 nodeTofragments 是講所有的 dom 節(jié)點放到文檔碎片中操作,最后一步,是把解析好的 dom 元素,從文檔碎片重新加入到頁面中,這兩步的具體方法,大家去下載我的源碼,看一下就明白了,有注釋。我就不再解釋了。

    我們來看一下第二步,編譯模板:

    complie(fragments) {

    //獲取所有節(jié)點

    const nodes = fragments.childNodes;

    [...nodes].forEach(ele => {

    if (this.isNodeElement(ele)) {

    //1. 編譯元素節(jié)點

    this.complieElement(ele)

    } else {

    //編譯文本節(jié)點

    this.complieText(ele)

    }

    //如果有子節(jié)點,循環(huán)遍歷,編譯指令

    if (ele.childNodes && ele.childNodes.length) {

    this.complie(ele)

    }

    })

    }

    我們要知道,模板可能有兩種情況,一種是文本節(jié)點(含有雙大括號的插值表達式)和元素節(jié)點(含有指令)。我們獲取所有節(jié)點后對每個節(jié)點進行判斷,如果是元素節(jié)點,則用解析元素節(jié)點的方法,如果是文本節(jié)點,則調(diào)用解析文本的方法。

    complieElement(node) {

    //1.獲取所有的屬性

    const attrs = node.attributes;

    //2.篩選出是屬性的

    [...attrs].forEach(attr => {

    //attr是一個對象,name是屬性名,value是屬性值

    const {name,value} = attr

    //判斷是否含有v-開頭 如:v-html

    if (name.startsWith("v-")) {

    //將指令分離 text, html, on:click

    const [, directive] = name.split("-")

    //處理on:click或bind:name的情況 on,click

    const [dirName, paramName] = directive.split(":")

    //編譯模板

    complieUtils[dirName](node, value, this.vm, paramName)

    //刪除屬性,在頁面中的dom中不會再顯示v-html這種指令的屬性

    node.removeAttribute(name)

    } else if (name.startsWith("@")) {

    // 如果是事件處理 @click=handleClick

    let [, paramName] = name.split(@);

    complieUtils[on](node, value, this.vm, paramName);

    node.removeAttribute(name);

    } else if (name.startsWith(":")) {

    // 如果是事件處理 :href=...

    let [, paramName] = name.split(:);

    complieUtils[bind](node, value, this.vm, paramName);

    node.removeAttribute(name);

    }

    })

    }

    我們在編譯模板中調(diào)用了 complieUtils[dirName](node, value, this.vm, paramName)方法,這是工具類中的一個方法,用于處理指令。

    我們再來看看文本節(jié)點,文本節(jié)點就相對比較簡單,只需要匹配{{}}形式的插值表達式就可以了,同樣的調(diào)用工具方法,來解析。

    complieText(node) {

    //1.獲取所有的文本內(nèi)容

    const text = node.textContent

    //匹配{{}}

    if (/\{\{(.+?)\}\}/.test(text)) {

    //編譯模板

    complieUtils[text](node, text, this.vm)

    }

    }

    上面用來這么多工具方法,我們來看看到底是什么。

    實現(xiàn) ComplieUtils 工具方法

    這個方法主要是對指令進行處理,獲取指令中的值,并在頁面中更新相應(yīng)的值,同時我們在這里要綁定 watcher 的回調(diào)函數(shù)。

    我來以 v-text 指令來解釋,其他指令都有注釋,大家自己看。

    import Watcher from ./watcher

    export const complieUtils = {

    //處理text指令

    text(node, expr, vm) {

    let value;

    if (/\{\{.+?\}\}/.test(expr)) {

    //處理 {{}}

    value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {

    //綁定觀察者/更新函數(shù)

    new Watcher(vm, args[1], => {

    //第二個參數(shù),傳入回調(diào)函數(shù)

    this.updater.updaterText(node, this.getContentVal(expr, vm))

    })

    return this.getValue(args[1], vm)

    })

    } else {

    //v-text

    new Watcher(vm, expr, (newVal) => {

    this.updater.updaterText(node, newVal)

    })

    //獲取到value值

    value = this.getValue(expr, vm)

    }

    //調(diào)用更新函數(shù)

    this.updater.updaterText(node, value)

    },

    }

    Text 處理函數(shù)是對 dom 元素的 TextContent 進行操作的,所以有兩種情況,一種是使用 v-text 指令,會更新元素的 textContent,另一種情況是{{}} 的插值表達式,也是更新元素的 textContent。

    在此方法中我們先判斷是哪一種情況,如果是 v-text 指令,那么就綁定一個 watcher 的回調(diào),獲取到 textContent 的值,調(diào)用 updater.updaterText 在下面講,是更新元素的方法。如果是雙大括號的話,我們就要對其進行特殊處理,首先是將雙大括號替換成指定的變量的值,同時為其綁定 watcher 的回調(diào)。

    //通過表達式, vm獲取data中的值, person.name

    getValue(expr, vm) {

    return expr.split(".").reduce((data, currentVal) => {

    return data[currentVal]

    }, vm.$data)

    },

    獲取 textContent 的值是用一個 reduce 函數(shù),用法在最后面的鏈接中,因為數(shù)據(jù)可能是 person.name 我們需要獲取到最深的對象的值。

    //更新dom元素的方法

    updater: {

    //更新文本

    updaterText(node, value) {

    node.textContent = value

    }

    }

    updater.updaterText更新dom的方法,其實就是對 textContent 重新賦值。

    我們再來將一下v-model指令,實現(xiàn)雙向的數(shù)據(jù)綁定,我們都知道,v-model其實實現(xiàn)的是 input 事件和 value 之間的語法糖。所以我們這里同樣的監(jiān)聽一下當前 dom 元素的 input 事件,當數(shù)據(jù)改變時,調(diào)用設(shè)置新值的方法:

    //處理model指令

    model(node, expr, vm) {

    const value = this.getValue(expr, vm)

    //綁定watcher

    new Watcher(vm, expr, (newVal) => {

    this.updater.updaterModel(node, newVal)

    })

    //雙向數(shù)據(jù)綁定

    node.addEventListener("input", (e) => {

    //設(shè)值方法

    this.setVal(expr, vm, e.target.value)

    })

    this.updater.updaterModel(node, value)

    },

    這個方法同樣是通過 reduce 方法,為對應(yīng)的變量設(shè)置成新的值,此時數(shù)據(jù)改變了,會自動調(diào)用更新視圖的方法,我們在之前已經(jīng)實現(xiàn)了。

    //通過表達式,vm,輸入框的值,實現(xiàn)設(shè)置值,input中v-model雙向數(shù)據(jù)綁定

    setVal(expr, vm, inputVal) {

    expr.split(".").reduce((data, currentVal) => {

    data[currentVal] = inputVal

    }, vm.$data)

    },

    實現(xiàn)VUE

    最后呢,我們就要來整合這些類和工具方法,在創(chuàng)建一個 VUE 實例的時候,我們先獲取 options 中的參數(shù),然后對起進行數(shù)據(jù)劫持和編譯模板:

    class Vue {

    constructor(options) {

    //獲取模板

    this.$el = options.el;

    //獲取data中的數(shù)據(jù)

    this.$data = options.data;

    //將對象中的屬性存起來,以便后續(xù)使用

    this.$options = options

    //1.數(shù)據(jù)劫持,設(shè)置setter/getter

    new Observer(this.$data)

    //2.編譯模板,解析指令

    new Complie(this.$el, this)

    }

    }

    此時我們想要使用 VUE 中的數(shù)據(jù),比如我們想要在 vm 對象中使用person.name, 必須用 this.$data.person.name 才能獲取到,如果我們想在 vm 對象中使用 this.person.name 直接修改數(shù)據(jù),就需要代理一下 this.$data 。其實就是將當前的 this.$data 中的數(shù)據(jù)放到全局中進行監(jiān)聽。

    export default class Vue {

    constructor(options) {

    //...

    //1.數(shù)據(jù)劫持,設(shè)置setter/getter

    //2.編譯模板,解析指令

    if (this.$el) { //如果有模板

    //代理this

    this.proxyData(this.$data)

    }

    }

    proxyData(data) {

    for (const key in data) {

    //將當前的數(shù)據(jù)放到全局指向中

    Object.defineProperty(this, key, {

    get {

    return data[key];

    },

    set(newVal) {

    data[key] = newVal

    }

    })

    }

    }

    }

    文章到了這里,就實現(xiàn)了一個簡易版的 VUE,建議大家反復(fù)學(xué)習,仔細體驗,細細品味。

    在文章的最后,我通過問、答的形式,來解答一些常見的面試題:

    問:什么時候頁面會重新渲染?

    答:數(shù)據(jù)發(fā)生改變,頁面就會重新渲染,但數(shù)據(jù)驅(qū)動視圖,數(shù)據(jù)必須先存在,然后才能實現(xiàn)數(shù)據(jù)綁定,改變數(shù)據(jù),頁面才會重新渲染。

    問:什么時候頁面不會重新渲染?

    答:有3種情況不會重新渲染:

    1、未經(jīng)聲明和未使用的變量,修改他們,都不會重新渲染頁面

    2、通過索引的方式和更改長度的方式更改數(shù)組,都不會重新渲染頁面

    3、增加和刪除對象的屬性,不會重新渲染頁面

    問:如何使 未聲明/未使用的變量、增加/刪除對象屬性可以使頁面重新渲染?

    答:添加利用 vm.$set/VUE.set,刪除利用vm.$delete/VUE.delete方法

    問:如何更改數(shù)組可以使頁面重新渲染?

    答:可以使用數(shù)組的變異方法(共 7 個):push、pop、unshift、shift、splice、sort、reverse

    問:數(shù)據(jù)更新后,頁面會立刻重新渲染么?

    答:更改數(shù)據(jù)后,頁面不會立刻重新渲染,頁面渲染的操作是異步執(zhí)行的,執(zhí)行完同步任務(wù)后,才會執(zhí)行異步的

    同步隊列,異步隊列(宏任務(wù)、微任務(wù))

    問:如果更改了數(shù)據(jù),想要在頁面重新渲染后再做操作,怎么辦?

    答:可以使用 vm.$nextTick 或 VUE.nextTick

    問:來介紹一下vm.$nextTick 和 VUE.nextTick 吧。

    答:我們來看個小例子就明白啦:

    {{ name }}

    const vm = new Vue({

    el: #app,

    data: {

    name: monk

    }

    })

    vm.name = the young monk;

    console.log(vm.name); // the young monk 此時數(shù)據(jù)已更改

    console.log(vm.$el.innerHTML); // monk 此時頁面還未重新渲染

    // 1. 使用vm.$nextTick

    vm.$nextTick( => {

    console.log(vm.$el.innerHTML); // the young monk 此時數(shù)據(jù)已更改

    })

    // 2. 使用Vue.nextTick

    Vue.nextTick( => {

    console.log(vm.$el.innerHTML); // the young monk 此時數(shù)據(jù)已更改

    })

    </script>

    問:vm.$nextTick 和 VUE.nextTick 有什么區(qū)別呢 ?

    答:VUE.nextTick 內(nèi)部函數(shù)的 this 指向 Window,vm.$nextTick 內(nèi)部函數(shù)的 this 指向 VUE 實例對象。

    Vue.nextTick(function {

    console.log(this); // window

    })

    vm.$nextTick(function {

    console.log(this); // vm實例

    })

    問:vm.$nextTick 和 VUE.nextTick 是通過什么實現(xiàn)的呢?

    答:二者都是等頁面渲染后執(zhí)行的任務(wù),都是使用微任務(wù)。

    if(typeof Promise !== undefined) {

    // 微任務(wù)

    // 首先看一下瀏覽器中有沒有promise

    // 因為IE瀏覽器中不能執(zhí)行Promise

    const p = Promise.resolve;

    } else if(typeof MutationObserver !== undefined) {

    // 微任務(wù)

    // 突變觀察

    // 監(jiān)聽文檔中文字的變化,如果文字有變化,就會執(zhí)行回調(diào)

    // vue的具體做法是:創(chuàng)建一個假節(jié)點,然后讓這個假節(jié)點稍微改動一下,就會執(zhí)行對應(yīng)的函數(shù)

    } else if(typeof setImmediate !== undefined) {

    // 宏任務(wù)

    // 只在IE下有

    } else {

    // 宏任務(wù)

    // 如果上面都不能執(zhí)行,那么則會調(diào)用setTimeout

    }

    同樣的這也是 VUE 的一個小缺點:VUE 一直是等主線程執(zhí)行完以后再執(zhí)行渲染任務(wù),如果主線程卡死,則永遠渲染不出來。

    問:利用 Object.defineProperty 實現(xiàn)響應(yīng)式有什么缺點?

    答:

    1、天生就需要進行遞歸

    2、監(jiān)聽不到數(shù)組不存在的索引的改變

    3、監(jiān)聽不到數(shù)組長度的改變

    4、監(jiān)聽不到對象的增刪

    版權(quán)聲明:本文為CSDN博主「愛編程的小和尚」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

    原文鏈接:

    https://blog.csdn.net/Newbie___/article/details/105973085

    ?雷軍:4G 手機已清倉,全力轉(zhuǎn) 5G;QQ音樂播放中途插語音廣告引熱議;Wine 5.9 發(fā)布 | 極客頭條

    ?中國 AI 應(yīng)用元年來了!

    ?新基建東風下,開發(fā)者這樣抓住工業(yè)互聯(lián)網(wǎng)風口!

    ?15 歲黑進系統(tǒng),發(fā)挑釁郵件意外獲 Offer,不惑之年捐出全部財產(chǎn),Twitter CEO 太牛了!

    ?避坑!使用 Kubernetes 最易犯的 10 個錯誤

    ?必讀!53個Python經(jīng)典面試題詳解

    ?贈書 | 1月以來 Tether 增發(fā)47億 USDT,美元都去哪兒了?

    掃描二維碼推送至手機訪問。

    版權(quán)聲明:本文由財神資訊-領(lǐng)先的體育資訊互動媒體轉(zhuǎn)載發(fā)布,如需刪除請聯(lián)系。

    本文鏈接:http://www.daniuzhishi.com/?id=44540

    “VUE 響應(yīng)式原理源碼:帶你一步精通 VUE | 原力計劃” 的相關(guān)文章

    【人物】塔瓦雷斯:十二年堅守,帶給烏拉圭足球革命性的影響

    【人物】塔瓦雷斯:十二年堅守,帶給烏拉圭足球革命性的影響

    作為本屆世界杯年紀最大、在位時間最長的主帥,71歲的塔瓦雷斯在過去十二年里成功扮演了“老師”角色,為烏拉圭足球帶來了革命性影響。 “同伊朗的比賽中,葡萄牙在場上有9名歐洲杯冠軍成員,這說明了他們的實力。當然,我們也做好了充分準備,大家已經(jīng)一起工作12年了。” 主帥...

    2022世界杯 | 烏拉圭對陣韓國 還有頗多“重慶足球元素”

    2022世界杯 | 烏拉圭對陣韓國 還有頗多“重慶足球元素”

     /////////// 最新比賽結(jié)果 ///////////       瑞士   1 : 0   喀麥隆        &nbs...

    2022足球世界杯排名表,2022足球世界杯賽程時間表?

    2022足球世界杯排名表,2022足球世界杯賽程時間表?

            感謝球迷朋友們一路的支持與陪伴,根據(jù)公眾號留言信息反饋比較多的是能不能建個交流群,應(yīng)大家的要求小編建立了一個球迷交流群,大家可以在這里分享一些賽事預(yù)告以及分析,轉(zhuǎn)會信息,游戲平臺介紹以及看球平臺    &...

    中超足球比分-保利尼奧引領(lǐng)三球逆轉(zhuǎn) 恒大3-1天海暫登榜首

    中超足球比分-保利尼奧引領(lǐng)三球逆轉(zhuǎn) 恒大3-1天海暫登榜首

    2019賽季中超聯(lián)賽第16輪賽事展開,廣州恒大淘寶客場3-1逆轉(zhuǎn)戰(zhàn)勝天津天海,恒大取得7連勝,暫時反超本輪還未出戰(zhàn)的國安升至積分榜頭名。身處降級區(qū)的天海在比賽前段同恒大形成積極對峙,第5分鐘,21歲的錢宇淼助楊旭...

    【關(guān)注】競彩網(wǎng)發(fā)布通知:暫停發(fā)布全國聯(lián)網(wǎng)單場競猜游戲足球賽程

    【關(guān)注】競彩網(wǎng)發(fā)布通知:暫停發(fā)布全國聯(lián)網(wǎng)單場競猜游戲足球賽程

    剛剛競彩網(wǎng)發(fā)布中國體育賽事公告,公告提到“由于各大主流足球聯(lián)賽受新冠肺炎疫情影響推遲,即日起暫停發(fā)布全國聯(lián)網(wǎng)單場競猜游戲(競彩)足球賽程?;謴?fù)時間視賽事情況另行通知,請關(guān)注競彩網(wǎng)公告?!?競彩停售對于體彩店主來說可謂是當頭一棒。引起彩票店主熱烈議論,紛紛表示 “撐不下去了...

    3.20各項足球賽事結(jié)果公告球賽比分大全

    001廣島三箭VS川崎前鋒 0:2 日職聯(lián) 002京都不死鳥VS東京FC 0:1 日職聯(lián) 003清水心跳VS神戶勝利船 0:0 日職聯(lián) 004大分三神VS山口雷法 1:1 日職聯(lián) 005水原三星VS江原FC 2:2 K聯(lián)賽 006浦和紅鉆VS磐田喜悅...

    ?