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

一文徹底搞懂Vue的MVVM響應(yīng)式原理

 更新時間:2022年06月28日 09:55:42   作者:DreamYum  
這篇文章主要介紹了一文徹底搞懂Vue的MVVM響應(yīng)式原理,vue則是采用發(fā)布者-訂閱者模式,通過Object.defineProperty()來劫持各個屬性的getter和setter,在數(shù)據(jù)變動時發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)

前言

這些天都在面試,每當我被面試官們問到Vue響應(yīng)式原理時,回答得都很膚淺。如果您在回答時也只是停留在MVVM框架是model層、view層和viewmodel層這樣的雙向數(shù)據(jù)綁定,那么建議您徹底搞定Vue的MVVM響應(yīng)式原理。
(全文約13900字,閱讀時間約25分鐘。建議有一定vue基礎(chǔ)后再閱讀)

怎么來的?

要想清楚的知道某件事物的原理,就該追根溯源,刨根問底。在Vue之前,各框架都是怎么去實現(xiàn)MVVM雙向綁定的呢?

大致分為以下幾種:

  • 發(fā)布者-訂閱者模式(backbone.js)臟值檢查(angular.js)數(shù)據(jù)劫持(vue.js)
  • 發(fā)布者-訂閱者模式,通過sub、pub實現(xiàn)視圖的監(jiān)聽綁定,通常的做法是vm.$set(‘property’, value)。

臟值檢查,內(nèi)部其實就是setnterval,當然,為了節(jié)約性能,不顯的那么low,一般是對特定的事件執(zhí)行臟值檢查:

DOM事件,如輸入文本、點擊按鈕(ng-click)XHR響應(yīng)事件($http)瀏覽器locaton 變更事件($location)Timer事件($timeout, $interval)執(zhí)行$digest()或 $apply()

vue則是采用發(fā)布者-訂閱者模式,通過Object.defineProperty()來劫持各個屬性的getter和setter,在數(shù)據(jù)變動時發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。

Vue的MVVM原理

話不多說,先上圖

首先,請盡可能記住這一張圖,并能夠自己畫出來,后面所有原理都是圍繞這張圖展開。感覺很懵逼對么?不過,相信許多人在Vue官方文檔里看過這張圖:

其實,這兩張圖要表達的是一個意思——二者都表示了雙向數(shù)據(jù)綁定的原理流程,官方文檔中展示的更為簡潔一些。看您更能接受哪種描述,后面自己實現(xiàn)響應(yīng)式原理后,這兩張圖都能記得住了。

這里就用第一張圖來介紹,在我們創(chuàng)建一個vue實例時,其實vue做了這些事情:

創(chuàng)建了入口函數(shù),分別new了一個數(shù)據(jù)觀察者Observer和一個指令解析器Compile;Compile解析所有DOM節(jié)點上的vue指令,提交到更新器Updater(實際上是一個對象);Updater把數(shù)據(jù)(如{{}},msg,@click)替換,完成頁面初始化渲染;Observer使用Object.defineProperty劫持數(shù)據(jù),其中的getter和setter通知變化給依賴器Dep;Dep中加入觀察者Watcher,當數(shù)據(jù)發(fā)生變化時,通知Watcher更新;Watcher取到舊值和新值,在回調(diào)函數(shù)中通知Updater更新視圖;Compile中每個指令都new了一個Watcher,用于觸發(fā)Watcher的回調(diào)函數(shù)進行更新。 簡單實現(xiàn)Vue的響應(yīng)式原理 完整源碼:詳見

按照前面的思路,下面我們來一步一步實現(xiàn)一個簡單的MVVM響應(yīng)式系統(tǒng),加深我們對響應(yīng)式原理的理解。

創(chuàng)建一個html示例

現(xiàn)在我們創(chuàng)建了一個簡單的Vue渲染示例,我們要做的就是使用自己的MVue去把里面的data、msg、htmlStr、methods中的數(shù)據(jù)都渲染到標簽上。完成數(shù)據(jù)驅(qū)動視圖、視圖驅(qū)動數(shù)據(jù)驅(qū)動視圖。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id ="app">  
        <h2>{{person.name}} -- {{person.age}}</h2>
        <h3>{{person.fav}}</h3>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
        </ul>
        <h3>{{msg}}</h3>
        <div v-text="person.fav"></div>
        <div v-text="msg"></div>
        <div v-html="htmlStr"></div>
        <input type="text" v-model="msg">
        <button v-on:click="handleClick">click</button>
        <button @click="handleClick">@click2</button>

    </div>
    <script src="./Observer.js"></script>
    <script src="./MVue.js"></script>
    <script>
     //創(chuàng)建Vue實例,得到 ViewModel
     const vm = new MVue({
        el: '#app',
        data: {
            person: {
                name: "我的vue",
                age: 18,
                fav: "坦克世界"
            },
            msg: "學習MVVM框架原理",
            htmlStr: "<h3>熱愛前端,金子總會發(fā)光</h3>"
        },
        methods: {
            handleClick() {
                console.log(this);
            }
        }
     });
    </script>
</body>
</html>

在MVue.js中創(chuàng)建MVue入口

class MVue {
  constructor(options) {
    this.$el = options.el;
    this.$data = options.data;
    this.$options = options;

    if (this.$el) {
      // 1、實現(xiàn)一個數(shù)據(jù)觀察者
      // 2、實現(xiàn)一個指令觀察者
      new Compile(this.$el, this);
    }
  }

思路:

首先自然是要構(gòu)建MVue這一個類,MVue類構(gòu)造函數(shù)中需要用到options參數(shù)和其中的el、data。
然后需要保證el存在條件下,先實現(xiàn)一個指令解析器Compile,后面再去實現(xiàn)Observer觀察者。
顯然,Compile應(yīng)該需要傳入MVue實例的el和整個MVue實例,用來解析標簽的指令。

創(chuàng)建Compile

思路:在解析標簽指令之前,我們首先做的是:

判斷el是不是元素節(jié)點,如果不是,就要取到el這個標簽,然后傳入vm實例;遞歸拿到所有子節(jié)點,便于下一步去解析它們。【注意:這一步會頻繁觸發(fā)頁面的回流和重繪,所以我們需要把節(jié)點先存入文檔碎片對象中,就相當于把他們放到了內(nèi)存中,減少了頁面的回流和重繪?!吭谖臋n碎片對象中編譯好模板;最后再把文檔碎片對象追加到根元素上。

class Compile {
  constructor(el, vm) {
    this.el = this.isElementNode(el) ? el : document.querySelector(el);
    this.vm = vm;
    // 獲取文檔碎片對象 放入內(nèi)存中會減少頁面的回流和重繪
    const fragment = this.node2Fragment(this.el);
    // 編譯模板
    this.compile(fragment);

    // 追加子元素到根元素
    this.el.appendChild(fragment);
  }

這里我們先自己定義了幾個方法:

  • 判斷是否是元素節(jié)點isElementNode(el)、
  • 存入文檔碎片對象node2Fragment(el)
  • 編譯模板compile(fragment)

分別在構(gòu)造函數(shù)之后去實現(xiàn):

 node2Fragment(el) {
    // 創(chuàng)建文檔碎片對象
    const f = document.createDocumentFragment();
    // 遞歸放入
    let firstChild;
    while ((firstChild = el.firstChild)) {
      f.appendChild(firstChild);
    }
    return f;
  }
  isElementNode(node) {
    return node.nodeType === 1;
  }

編譯模板compile(fragment)實現(xiàn)思路:遞歸獲取所有子節(jié)點,判斷節(jié)點是元素節(jié)點還是文本節(jié)點,再分別定義兩個方法compileElement(child)compileText(child)去處理這兩種節(jié)點。

compile(fragment) {
    // 獲取子節(jié)點
    const childNodes = fragment.childNodes;
    [...childNodes].forEach((child) => {
      if (this.isElementNode(child)) {
        // 是元素節(jié)點
        // 編譯元素節(jié)點
        // console.log("元素節(jié)點",child);
        this.compileElement(child);
      } else {
        // 是文本節(jié)點
        // 編譯文本節(jié)點
        // console.log("文本節(jié)點", child);
        this.compileText(child);
      }

      // 一層一層遞歸遍歷
      if (child.childNodes && child.childNodes.length) {
        this.compile(child);
      }
    });
  }

好了,現(xiàn)在Compile的一個基本框架已經(jīng)搭好了。希望看到這里的您還沒有犯困,打起精神來!現(xiàn)在,我們繼續(xù)往下淦元素節(jié)點和文本節(jié)點的處理。

1.處理元素節(jié)點compileElement(child)

思路:

拿到標簽里的每個vue指令,如v-text v-html v-model v-on:click,顯然它們都是以v-開頭的,當然還有@開頭的指令也不要忘記把節(jié)點、節(jié)點值、vm實例、(on的事件名)傳入compileUtil對象,后面用它處理每個指令,屬性對應(yīng)指令方法;別忘了,最后的視圖標簽上是沒有vue指令的,所以我們要把它們從節(jié)點屬性中刪去。

compileElement(node) {
    const attributes = node.attributes;
    [...attributes].forEach((attr) => {
      const { name, value } = attr;
      if (this.isDirective(name)) {
        // 是一個指令 v-text v-html v-model v-on:click
        const [, directive] = name.split("-"); // text html model on:click
        const [dirName, eventName] = directive.split(":"); // text html model on
        // 更新數(shù)據(jù) 數(shù)據(jù)驅(qū)動視圖
        compileUtil[dirName](node, value, this.vm, eventName);

        // 刪除有指令標簽上的屬性
        node.removeAttribute("v-" + directive);
      } else if (this.isEventName(name)) {
        // @click='handleClick'
        let [, eventName] = name.split('@');
        compileUtil["on"](node, value, this.vm, eventName);
      }
    });
  }

判斷是否是指令,以v-開頭

isDirective(attrName) {
    return attrName.startsWith("v-");
  }

2.處理文本節(jié)點compileText(child)

主要使用正則匹配雙大括號即可:

compileText(node) {
    // {{}} v-text
    const content = node.textContent;

    if (/\{\{(.+?)\}\}/.test(content)) {
      compileUtil["text"](node, content, this.vm);
    }
  }

3.實現(xiàn)compileUtil指令處理

思路:

每個指令對應(yīng)各自方法,除了on需要額外傳入事件名稱,其他的指令處理函數(shù)只需要傳節(jié)點、值(或表達式expr)、vm

實例:

const compileUtil = {
  text(node, expr, vm) {

  },
  html(node, expr, vm) {
   
  },
  model(node, expr, vm) {
   
  },
  on(node, expr, vm, eventName) {
  
  }
};

沒有一下子放出代碼來的話,骨架原來這么簡單啊,繼續(xù)逐個擊破它們!
v-html指令處理,思路:拿到值,把值傳給updater更新器,更新,完事兒。

html(node, expr, vm) {
    const value = this.getVal(expr, vm);
    this.updater.htmlUpdater(node, value);
  },

v-model指令處理,同上。先實現(xiàn)數(shù)據(jù)=>視圖這條線,雙向綁定最后實現(xiàn)。

model(node, expr, vm) {
    const value = this.getVal(expr, vm);
    this.updater.modelUpdater(node, value);
  },

比較復(fù)雜的,v-on,思路:獲取事件名,從methods中取到對應(yīng)的函數(shù),添加到事件中,注意this要綁定給vm實例,false默認事件冒泡。

on(node, expr, vm, eventName) {
    // 獲取事件名, 從method里面取函數(shù)
    let fn = vm.$options.methods && vm.$options.methods[expr];
    node.addEventListener(eventName, fn.bind(vm), false)
  },

v-text指令處理:

text(node, expr, vm) {
    // expr:msg: "學習MVVM框架原理"
    // 對傳入不同的字符串不同操作 <div v-text="person.name"></div>
    // {{}}
    let value;
    if (expr.indexOf('{{') !== -1) {
        // {{person.name}} -- {{person.age}}
        value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
            return this.getVal(args[1], vm)
        })
    } else {
        value = this.getVal(expr, vm);
    }
    this.updater.textUpdater(node, value);
  },

用到args這個數(shù)組,console.log一下args,發(fā)現(xiàn)args[1]就有我們要找的具體屬性:

例如,取到person.name后,傳入到this.getVal('person.name',vm),最后能取到vm.$data.person.name

怎么拿到它們對應(yīng)的值呢?

顯然,不論是htmlStr、msg、person,它們都在實例vm的data內(nèi),在自定義方法getVal中,可以使用split分割小圓點“.”得到數(shù)組,再用高逼格的reduce方法去遍歷找到data每個屬性(對象)下的每個屬性的值,像這樣:

getVal(expr, vm) {
    return expr.split(".").reduce((data, currentVal) => {
      return data[currentVal];
    }, vm.$data);
  },

(不記得怎么用reduce?請在右上角新建標簽頁,去CSDN上補一補。)進階拿到雙大括號內(nèi)對應(yīng)的屬性的值:

getContentVal(expr, vm) {
    return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
      console.log(args);
      return this.getVal(args[1], vm);
    });
  },

更新器Updater更新數(shù)據(jù)

在指令方法的后面接著創(chuàng)建一個updater屬性,實則是一個類,我們把它親切地稱作更新器,長得還很一目了然,您馬上就能記住它的樣子:

// 更新的函數(shù)
  updater: {
    textUpdater(node, value) {
      node.textContent = value;
    },
    htmlUpdater(node, value) {
      node.innerHTML = value;
    },
    modelUpdater(node, value){
      node.value = value;
    }
  },

在每個指令方法取到值后,更新到node節(jié)點上。

至此,我們已經(jīng)完成了原理圖上的MVVM到Compile到Updater這一條線:

實現(xiàn)數(shù)據(jù)觀察者Observer

class MVue {
  constructor(options) {
    this.$el = options.el;
    this.$data = options.data;
    this.$options = options;

    if (this.$el) {
      // 1、實現(xiàn)一個數(shù)據(jù)觀察者
      new Observer(this.$data);
      // 2、實現(xiàn)一個指令觀察者
      new Compile(this.$el, this);
    }
  }

Observer類構(gòu)造函數(shù)應(yīng)該傳什么給它?對,Observer要監(jiān)聽所有數(shù)據(jù),所以我們將vm實例的data作為參數(shù)傳入。

  • 遞歸,將data中所有的屬性、對象、子對象……都遍歷出來
  • 對每個key,使用Object.defineProperty劫持數(shù)據(jù)(Object.defineProperty()的作用就是直接在一個對象上定義一個新屬性,或者修改一個已經(jīng)存在的屬性)
  • Object.defineProperty下有g(shù)et方法和set方法,也就是官方原理圖上的getter和stter啦
  • 在劫持數(shù)據(jù)之前,創(chuàng)建依賴器Dep實例dep
  • 對于gettter,訂閱數(shù)據(jù)變化時,往dep中添加觀察者;
  • 對于setter,當數(shù)據(jù)變化時,將newVal賦值為新值,并用notify通知dep變化。(此處正好對應(yīng)官方原理圖)

4、5、6這最后三點可以說是MVVM實現(xiàn)中最關(guān)鍵、最巧妙的3步,正是這畫龍點睛的三筆,把整個系統(tǒng)橋梁成功架起來,注意它們各自放置在代碼中位置。

class Observer {
  constructor(data) {
    this.observer(data);
  }
  observer(data) {
    /**
        {
            person:{
                name:'張三',
                fav: {
                    a: '愛好1',
                    b: '愛好2'
                }
            }
        }
         */
    if (data && typeof data === "object") {
      Object.keys(data).forEach((key) => {
        this.defineReactive(data, key, data[key]);
      });
    }
  }
  defineReactive(obj, key, value) {
    // 遞歸遍歷
    this.observer(value);
    const dep = new Dep();
    // 劫持數(shù)據(jù)
    Object.defineProperty(obj, key, {
      // 是否可遍歷
      enumerable: true,
      // 是否可以更改編寫
      configurable: false,
      // 編譯之前,初始化的時候
      get() {
        // 訂閱數(shù)據(jù)變化時,往Dep中添加觀察者
        Dep.target && dep.addSub(Dep.target);
        return value;
      },
      // 外界修改數(shù)據(jù)的時候
      set: (newVal) => {
        // 新值也要劫持
        this.observer(newVal); // 這里的this要指向當前的實例,所以改用箭頭函數(shù)向上查找
        // 判斷新值是否有變化
        if (newVal !== value) {
          value = newVal;
        }
        // 告訴Dep通知變化
        dep.notify();
      },
    });
  }
}

數(shù)據(jù)依賴器Dep

主要作用:

  • 收集要更新的觀察者
  • 通知每個觀察者去更新
// 數(shù)據(jù)依賴器
class Dep {
    constructor() {
        this.subs = [];
    }
    // 收集觀察者
    addSub(watcher) {
        this.subs.push(watcher);
    }
    // 通知觀察者去更新
    notify() {
        console.log("通知了觀察者", this.subs);
        this.subs.forEach(w =>w.update())
    }
}

觀察者Watcher

注意Dep.target = this;這一步,是為了把觀察者掛載到Dep實例上,關(guān)聯(lián)起來。所以當觀察者Watcher獲取舊值后,應(yīng)該解除關(guān)聯(lián),否則會重復(fù)地添加觀察者,以下是未取消關(guān)聯(lián)的錯誤示范:

最后,使用callback回調(diào)函數(shù)傳遞要處理的新值給Updater即可。

class Watcher {
  constructor(vm, expr, callback) {
    // 把新值通過cb傳出去
    this.vm = vm;
    this.expr = expr;
    this.callback = callback;
    // 先把舊值保存起來
    this.oldVal = this.getOldVal();
  }
  getOldVal() {
    // 把觀察者掛載到Dep實例上,關(guān)聯(lián)起來
    Dep.target = this;
    const oldVal = compileUtil.getVal(this.expr, this.vm);
    // 獲取舊值后,取消關(guān)聯(lián),就不會重復(fù)添加
    Dep.target = null;
    return oldVal;
  }
  update() {
    // 更新,要取舊值和新值
    const newVal = compileUtil.getVal(this.expr, this.vm);
    if (newVal !== this.oldVal) {
        this.callback(newVal);
    }
  }
}

如何Updater如何接收從Watcher傳來的新值做回調(diào)處理呢?

只需要在剛剛寫好的compileUtil對象的每個指令處理方法內(nèi)都new(添加)一個Watcher實例即可。注意text指令方法下new Watcher實例的value參數(shù),可以用args[1]傳入,重新處理newVal。

const compileUtil = {
  getVal(expr, vm) {
    return expr.split(".").reduce((data, currentVal) => {
      return data[currentVal];
    }, vm.$data);
  },
  setVal(expr, vm, inputVal) {
    return expr.split(".").reduce((data, currentVal) => {
      data[currentVal] = inputVal; // 把當前新值復(fù)制給舊值
    }, vm.$data);
  },
  getContentVal(expr, vm) {
    return expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
      console.log(args);
      return this.getVal(args[1], vm);
    });
  },
  text(node, expr, vm) {
    // expr:msg: "學習MVVM框架原理"
    // 對傳入不同的字符串不同操作 <div v-text="person.name"></div>
    // {{}}
    let value;
    if (expr.indexOf('{{') !== -1) {
        // {{person.name}} -- {{person.age}}
        value = expr.replace(/\{\{(.+?)\}\}/g, (...args) => {
            new Watcher(vm, args[1], () => {
              // 額外處理expr: {{person.name}} -- {{person.age}}
              // 還要重新處理newVal
              this.updater.textUpdater(node, this.getContentVal(expr, vm));
            });
            return this.getVal(args[1], vm)
        })
    } else {
        value = this.getVal(expr, vm);
    }
    this.updater.textUpdater(node, value);
  },
  html(node, expr, vm) {
    const value = this.getVal(expr, vm);
    // 綁定觀察者,將來數(shù)據(jù)發(fā)生變化 出發(fā)這里的回調(diào) 進行更新
    new Watcher(vm, expr, newVal => {
        this.updater.htmlUpdater(node, newVal);
    })
    this.updater.htmlUpdater(node, value);
  },
  model(node, expr, vm) {
    const value = this.getVal(expr, vm);
    // 綁定更新函數(shù) 數(shù)據(jù)=>驅(qū)動視圖
    new Watcher(vm, expr, (newVal) => {
      this.updater.modelUpdater(node, newVal);
    });
    // 視圖 => 數(shù)據(jù) => 視圖
    node.addEventListener('input', e => {
        // 設(shè)置值
        this.setVal(expr, vm, e.target.value);
    })
    this.updater.modelUpdater(node, value);
  },
  on(node, expr, vm, eventName) {
    // 獲取事件名, 從method里面取函數(shù)
    let fn = vm.$options.methods && vm.$options.methods[expr];
    node.addEventListener(eventName, fn.bind(vm), false)
  },
  bind(node, expr, vm, attrName) {
      // 類似on。。。
  },
  // 更新的函數(shù)
  updater: {
    textUpdater(node, value) {
      node.textContent = value;
    },
    htmlUpdater(node, value) {
      node.innerHTML = value;
    },
    modelUpdater(node, value){
      node.value = value;
    }
  },
};

實現(xiàn)視圖驅(qū)動數(shù)據(jù)驅(qū)動視圖

還是借著上面這個代碼塊,我們只需要在model指令方法下,為input標簽綁定事件,并自定義setVal方法為node賦值即可。

到這里,我們已經(jīng)基本完整實現(xiàn)了Vue的MVVM雙向數(shù)據(jù)綁定

小改進:

在MVue實例中,我們一開始使用的是$data獲取到數(shù)據(jù),這里可以做一層代理proxy,便于我們省略$data

methods: {
            handleClick() {
                // console.log(this);
                this.person.name = "這是做了一層代理" 
                // 把this.$data 代理成 this
                this.$data.person.name = "數(shù)據(jù)更改了"
            }
        }

還是使用Object.defineProperty數(shù)據(jù)劫持,遍歷data下的每個key,讓getter返回data[key],setter設(shè)置data[key]直接等于newVal即可。

class MVue {
  constructor(options) {
    this.$el = options.el;
    this.$data = options.data;
    this.$options = options;

    if (this.$el) {
      // 1、實現(xiàn)一個數(shù)據(jù)觀察者
      new Observer(this.$data);
      // 2、實現(xiàn)一個指令觀察者
      new Compile(this.$el, this);
      this.proxyData(this.$data);
    }
  }
  proxyData(data) {
      for(const key in data) {
          Object.defineProperty(this, key, {
              get() {
                  return data[key]
              },
              set(newVal) {
                data[key] = newVal;
              }
          })
      }
  }
}

總結(jié)

再次體會官方文檔對響應(yīng)式原理的描述:

當我們把一個普通的 JavaScript 對象傳入 Vue 實例作為 data 選項,Vue 將遍歷此對象所有的 property,并使用 Object.defineProperty 把這些 property 全部轉(zhuǎn)為 getter/setter。Object.defineProperty 是 ES5 中一個無法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。

這些 getter/setter 對用戶來說是不可見的,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴,在 property
被訪問和修改時通知變更。這里需要注意的是不同瀏覽器在控制臺打印數(shù)據(jù)對象時對 getter/setter 的格式化并不同,所以建議安裝
vue-devtools 來獲取對檢查數(shù)據(jù)更加友好的用戶界面。

每個組件實例都對應(yīng)一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù) property 記錄為依賴。之后當依賴項的
setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。

以及開頭時我自己總結(jié)的原理描述:

在我們創(chuàng)建一個vue實例時,其實vue做了這些事情:

創(chuàng)建了入口函數(shù),分別new了一個數(shù)據(jù)觀察者

  • Observer和一個指令解析器Compile;
  • Compile解析所有DOM節(jié)點上的vue指令,提交到更新器Updater(實際上是一個對象);
  • Updater把數(shù)據(jù)(如{{}},msg,@click)替換,完成頁面初始化渲染;Observer使用Object.defineProperty劫持數(shù)據(jù),其中的getter和setter通知變化給依賴器Dep;
  • Dep中加入觀察者Watcher,當數(shù)據(jù)發(fā)生變化時,通知Watcher更新;
  • Watcher取到舊值和新值,在回調(diào)函數(shù)中通知Updater更新視圖;
  • Compile中每個指令都new了一個Watcher,用于觸發(fā)Watcher的回調(diào)函數(shù)進行更新。

在實現(xiàn)代碼的過程中,我們能深刻地體會到Vue的數(shù)據(jù)驅(qū)動視圖,視圖驅(qū)動數(shù)據(jù)驅(qū)動視圖 這一核心的巧妙,也知道了Object.defineProperty具體應(yīng)用場景。

到此這篇關(guān)于一文徹底搞懂Vue的MVVM響應(yīng)式原理的文章就介紹到這了,更多相關(guān) Vue的MVVM響應(yīng)式 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue全局引入scss(mixin)

    vue全局引入scss(mixin)

    本文主要介紹了?vue全局引入scss,我們在寫VUE的時候,會使用scss,也會做一些通用樣式,方便使用,在寫好的通用樣式的時候,每次都要單文件導(dǎo)入,剛開始寫的時候,感覺還好,后面工程量大了后,就顯得麻煩,那么本文就全局導(dǎo)入scss樣式,下面來看詳細內(nèi)容,需要的朋友可以參考一下
    2021-11-11
  • 詳解Vue2+Echarts實現(xiàn)多種圖表數(shù)據(jù)可視化Dashboard(附源碼)

    詳解Vue2+Echarts實現(xiàn)多種圖表數(shù)據(jù)可視化Dashboard(附源碼)

    本篇文章主要介紹了詳解Vue2+Echarts實現(xiàn)多種圖表數(shù)據(jù)可視化Dashboard(附源碼),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03
  • vue實現(xiàn)PC端錄音功能的實例代碼

    vue實現(xiàn)PC端錄音功能的實例代碼

    這篇文章主要介紹了vue實現(xiàn)PC端錄音功能的實例代碼,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-06-06
  • vue2和vue3組件v-model區(qū)別詳析

    vue2和vue3組件v-model區(qū)別詳析

    v-model通常用于input的雙向數(shù)據(jù)綁定,它并不會向子組件傳遞數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于vue2和vue3組件v-model區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • vue 驗證兩次輸入的密碼是否一致的方法示例

    vue 驗證兩次輸入的密碼是否一致的方法示例

    這篇文章主要介紹了vue 驗證兩次輸入的密碼是否一致的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • 快速解決Vue項目在IE瀏覽器中顯示空白的問題

    快速解決Vue項目在IE瀏覽器中顯示空白的問題

    今天小編就為大家分享一篇快速解決Vue項目在IE瀏覽器中顯示空白的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • 在vue中解決提示警告 for循環(huán)報錯的方法

    在vue中解決提示警告 for循環(huán)報錯的方法

    今天小編就為大家分享一篇在vue中解決提示警告 for循環(huán)報錯的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue實現(xiàn)用戶動態(tài)權(quán)限登錄的代碼示例

    vue實現(xiàn)用戶動態(tài)權(quán)限登錄的代碼示例

    這篇文章主要介紹了vue如何實現(xiàn)用戶動態(tài)權(quán)限登錄,文中的代碼示例介紹的非常詳細,對大家學習vue有一定的幫助,需要的朋友可以參考閱讀
    2023-05-05
  • vue3.0實現(xiàn)移動端電子簽名組件

    vue3.0實現(xiàn)移動端電子簽名組件

    這篇文章主要為大家詳細介紹了vue3.0實現(xiàn)移動端電子簽名組件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Vue修改mint-ui默認樣式的方法

    Vue修改mint-ui默認樣式的方法

    下面小編就為大家分享一篇Vue修改mint-ui默認樣式的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02

最新評論