最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue響應(yīng)式原理的示例詳解

 更新時(shí)間:2022年03月07日 09:09:18   作者:Zuckjet  
Vue 最獨(dú)特的特性之一,是非侵入式的響應(yīng)系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 JavaScript 對(duì)象。而當(dāng)你修改它們時(shí),視圖會(huì)進(jìn)行更新。本文將通過(guò)示例詳解Vue的響應(yīng)式原理,感興趣的可以了解一下

Vue 最獨(dú)特的特性之一,是非侵入式的響應(yīng)系統(tǒng)。數(shù)據(jù)模型僅僅是普通的 JavaScript 對(duì)象。而當(dāng)你修改它們時(shí),視圖會(huì)進(jìn)行更新。聊到 Vue 響應(yīng)式實(shí)現(xiàn)原理,眾多開(kāi)發(fā)者都知道實(shí)現(xiàn)的關(guān)鍵在于利用 Object.defineProperty , 但具體又是如何實(shí)現(xiàn)的呢,今天我們來(lái)一探究竟。

為了通俗易懂,我們還是從一個(gè)小的示例開(kāi)始:

<body>
  <div id="app">
    {{ message }}
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })
</script>
</body>

我們已經(jīng)成功創(chuàng)建了第一個(gè) Vue 應(yīng)用!看起來(lái)這跟渲染一個(gè)字符串模板非常類似,但是 Vue 在背后做了大量工作?,F(xiàn)在數(shù)據(jù)和 DOM 已經(jīng)被建立了關(guān)聯(lián),所有東西都是響應(yīng)式的。我們要怎么確認(rèn)呢?打開(kāi)你的瀏覽器的 JavaScript 控制臺(tái) (就在這個(gè)頁(yè)面打開(kāi)),并修改 app.message的值,你將看到上例相應(yīng)地更新。修改數(shù)據(jù)便會(huì)自動(dòng)更新,Vue 是如何做到的呢?

通過(guò) Vue 構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例時(shí),會(huì)有執(zhí)行一個(gè)初始化的操作:

function Vue (options) {
    this._init(options);
}

這個(gè) _init初始化函數(shù)內(nèi)部會(huì)初始化生命周期、事件、渲染函數(shù)、狀態(tài)等等:

initLifecycle(vm);
      initEvents(vm);
      initRender(vm);
      callHook(vm, 'beforeCreate');
      initInjections(vm);
      initState(vm);
      initProvide(vm);
      callHook(vm, 'created');

因?yàn)楸疚牡闹黝}是響應(yīng)式原理,因此我們只關(guān)注 initState(vm) 即可。它的關(guān)鍵調(diào)用步驟如下:

function initState (vm) {
  initData(vm);
}

function initData(vm) {
  // data就是我們創(chuàng)建 Vue實(shí)例傳入的 {message: 'Hello Vue!'}
  observe(data, true /* asRootData */);
}

function observe (value, asRootData) {
  ob = new Observer(value);
}

var Observer = function Observer (value) {
  this.walk(value);
}

Observer.prototype.walk = function walk (obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    // 實(shí)現(xiàn)響應(yīng)式關(guān)鍵函數(shù)
    defineReactive$$1(obj, keys[i]);
  }
};
}

我們來(lái)總結(jié)一下上面 initState(vm)流程。初始化狀態(tài)的時(shí)候會(huì)對(duì)應(yīng)用的數(shù)據(jù)進(jìn)行檢測(cè),即創(chuàng)建一個(gè) Observer 實(shí)例,其構(gòu)造函數(shù)內(nèi)部會(huì)執(zhí)行原型上的 walk方法。walk方法的主要作用便是 遍歷數(shù)據(jù)的所有屬性,并把每個(gè)屬性轉(zhuǎn)換成響應(yīng)式,而這轉(zhuǎn)換的工作主要由 defineReactive$$1 函數(shù)完成。

function defineReactive$$1(obj, key, val) {
  var dep = new Dep();
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value
    },
    set: function reactiveSetter(newVal) {
      var value = getter ? getter.call(obj) : val;
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (customSetter) {
        customSetter();
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) { return }
      if (setter) {
        setter.call(obj, newVal);
      } else {
        val = newVal;
      }
      childOb = !shallow && observe(newVal);
      dep.notify();
    }
  });
}

defineReactive$$1函數(shù)內(nèi)部使用Object.defineProperty 來(lái)監(jiān)測(cè)數(shù)據(jù)的變化。每當(dāng)從 obj 的 key 中讀取數(shù)據(jù)時(shí),get 函數(shù)被觸發(fā);每當(dāng)往 obj 的 key 中設(shè)置數(shù)據(jù)時(shí),set 函數(shù)被觸發(fā)。我們說(shuō)修改數(shù)據(jù)觸發(fā) set 函數(shù),那么 set 函數(shù)是如何更新視圖的呢?拿本文開(kāi)頭示例分析:

<div id="app">
    {{ message }}
</div>

該模板使用了數(shù)據(jù) message, 當(dāng) message 的值發(fā)生改變的時(shí)候,應(yīng)用中所有使用到 message 的視圖都能觸發(fā)更新。在 Vue 的內(nèi)部實(shí)現(xiàn)中,先是收集依賴,即把用到數(shù)據(jù) message 的地方收集起來(lái),然后等數(shù)據(jù)發(fā)生改變的時(shí)候,把之前收集的依賴全部觸發(fā)一遍就可以了。也就是說(shuō)我們?cè)谏鲜龅?get 函數(shù)中收集依賴,在 set 函數(shù)中觸發(fā)視圖更新。那接下來(lái)的重點(diǎn)就是分析 get 函數(shù)和 set 函數(shù)了。先看 get 函數(shù),其關(guān)鍵調(diào)用如下:

get: function reactiveGetter () {
        if (Dep.target) {
          dep.depend();
        }
 }
 
Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
 };
 
Watcher.prototype.addDep = function addDep (dep) {
  dep.addSub(this);
}

 Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
 };
 其中 Dep 構(gòu)造函數(shù)如下:
 var Dep = function Dep () {
   this.id = uid++;
   this.subs = [];
 };

上述代碼中Dep.target的值是一個(gè)Watcher實(shí)例,稍后我們?cè)俜治鏊呛螘r(shí)被賦值的。我們用一句話總結(jié) get 函數(shù)所做的工作:把當(dāng)前 Watcher 實(shí)例(也就是Dep.target)添加到 Dep 實(shí)例的 subs 數(shù)組中。在繼續(xù)分析 get 函數(shù)前,我們需要弄清楚 Dep.target 的值何時(shí)被賦值為 Watcher 實(shí)例,這里我們需要從 mountComponent這個(gè)函數(shù)開(kāi)始分析:

function mountComponent (vm, el, hydrating) {
  updateComponent = function () {
    vm._update(vm._render(), hydrating);
  };
  new Watcher(vm, updateComponent, noop, xxx);
}
// Wather構(gòu)造函數(shù)下
var Watcher = function Watcher (vm, expOrFn, cb) {
  if (typeof expOrFn === 'function') {
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
  }
   this.value = this.get();
}

Watcher.prototype.get = function get () {
   pushTarget(this);
   value = this.getter.call(vm, vm);
}

function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
}

由上述代碼我們知道m(xù)ountComponent函數(shù)會(huì)創(chuàng)建一個(gè) Watcher 實(shí)例,在其構(gòu)造函數(shù)中最終會(huì)調(diào)用 pushTarget函數(shù),把當(dāng)前 Watcher 實(shí)例賦值給 Dep.target。另外我們注意到,創(chuàng)建 Watcher 實(shí)例這個(gè)動(dòng)作是發(fā)生在函數(shù)mountComponent內(nèi)部,也就是說(shuō) Watcher 實(shí)例是組件級(jí)別的粒度,而不是說(shuō)任何用到數(shù)據(jù)的地方都新建一個(gè) Watcher 實(shí)例?,F(xiàn)在我們?cè)賮?lái)看看 set 函數(shù)的主要調(diào)用過(guò)程:

set: function reactiveSetter (newVal) {
  dep.notify();
}

Dep.prototype.notify = function notify () {
   var subs = this.subs.slice();
   for (var i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
}

Watcher.prototype.update = function update () {
  queueWatcher(this);
}

 Watcher.prototype.update = function update () {
   // queue是一個(gè)全局?jǐn)?shù)組
   queue.push(watcher);
   nextTick(flushSchedulerQueue);
 }
 
 // flushSchedulerQueue是一個(gè)全局函數(shù)
 function flushSchedulerQueue () {
    for (index = 0; index < queue.length; index++) {
      watcher = queue[index];
      watcher.run();
    }
 }
 
Watcher.prototype.run = function run () {
   var value = this.get();
}

set 函數(shù)內(nèi)容有點(diǎn)長(zhǎng),但上述代碼都是精簡(jiǎn)過(guò)的,應(yīng)該不難理解。當(dāng)改變應(yīng)用數(shù)據(jù)的時(shí)候,觸發(fā) set 函數(shù)執(zhí)行。它會(huì)調(diào)用 Dep 實(shí)例的 notify()方法,而 notify 方法又會(huì)把當(dāng)前 Dep 實(shí)例收集的所有 Watcher 實(shí)例的 update 方法調(diào)用一遍,以達(dá)到更新所有用到該數(shù)據(jù)的視圖部分。我們繼續(xù)看 Watcher 實(shí)例的 update 方法做了什么。update 方法會(huì)把當(dāng)前的 watcher 添加到數(shù)組 queue 中,然后把 queue 中每個(gè) watcher 的 run 方法執(zhí)行一遍。run 方法內(nèi)部會(huì)執(zhí)行 Wather 原型上的 get 方法,后續(xù)的調(diào)用在前文分析 mountComponent 函數(shù)中都有描述,在此就不再贅述??偨Y(jié)來(lái)說(shuō),最終 update 方法會(huì)觸發(fā) updateComponent函數(shù):

updateComponent = function () {
  vm._update(vm._render(), hydrating);
};

Vue.prototype._update = function (vnode, hydrating) {
  vm.$el = vm.__patch__(prevVnode, vnode);
}

這里我們注意到 _update 函數(shù)的第一個(gè)參數(shù)是 vnode 。vnode 顧名思義是虛擬節(jié)點(diǎn)的意思,它是一個(gè)普通對(duì)象,該對(duì)象的屬性上保存了生成 DOM 節(jié)點(diǎn)所需要數(shù)據(jù)。說(shuō)到虛擬節(jié)點(diǎn)你是不是很容易就聯(lián)想到虛擬 DOM 了呢,沒(méi)錯(cuò) Vue 中也使用了虛擬 DOM。前文說(shuō)到 Wather 是和組件相關(guān)的,組件內(nèi)部的更新就用虛擬 DOM 進(jìn)行對(duì)比和渲染。_update 函數(shù)內(nèi)部調(diào)用了 patch 函數(shù),通過(guò)該函數(shù)對(duì)比新舊兩個(gè) vnode 之間的不同,然后根據(jù)對(duì)比結(jié)果找出需要更新的節(jié)點(diǎn)進(jìn)行更新。

注:本文分析示例基于 Vue v2.6.14 版本。

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

相關(guān)文章

  • vue 解決setTimeOut和setInterval函數(shù)無(wú)效報(bào)錯(cuò)的問(wèn)題

    vue 解決setTimeOut和setInterval函數(shù)無(wú)效報(bào)錯(cuò)的問(wèn)題

    這篇文章主要介紹了vue 解決setTimeOut和setInterval函數(shù)無(wú)效報(bào)錯(cuò)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • 在vue中實(shí)現(xiàn)跨域方法小結(jié)

    在vue中實(shí)現(xiàn)跨域方法小結(jié)

    在Web開(kāi)發(fā)中,跨域問(wèn)題是一大挑戰(zhàn),跨域是指網(wǎng)絡(luò)請(qǐng)求從一個(gè)域名發(fā)起,而請(qǐng)求的目標(biāo)資源位于另一個(gè)不同的域名下,通常使用Vue CLI的代理來(lái)解決跨域問(wèn)題,而在生產(chǎn)環(huán)境中,可以通過(guò)后端配置CORS或使用Nginx反向代理,或者通過(guò)服務(wù)器中轉(zhuǎn)來(lái)解決跨域問(wèn)題
    2023-10-10
  • Element?Table行的動(dòng)態(tài)合并及數(shù)據(jù)編輯示例

    Element?Table行的動(dòng)態(tài)合并及數(shù)據(jù)編輯示例

    這篇文章主要為大家介紹了Element?Table行的動(dòng)態(tài)合并及數(shù)據(jù)編輯示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • element表格行列拖拽的實(shí)現(xiàn)示例

    element表格行列拖拽的實(shí)現(xiàn)示例

    本文主要介紹了element表格行列拖拽的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Vue使用v-viewer插件實(shí)現(xiàn)圖片預(yù)覽和縮放和旋轉(zhuǎn)等功能(推薦)

    Vue使用v-viewer插件實(shí)現(xiàn)圖片預(yù)覽和縮放和旋轉(zhuǎn)等功能(推薦)

    v-viewer是一個(gè)基于viewerjs封裝的vue圖片預(yù)覽組件,有預(yù)覽縮放拉伸旋轉(zhuǎn)切換拖拽等功能,支持配置化,這篇文章主要介紹了Vue使用v-viewer插件實(shí)現(xiàn)圖片預(yù)覽和縮放和旋轉(zhuǎn)等功能,需要的朋友可以參考下
    2023-02-02
  • 使用vue寫(xiě)一個(gè)翻頁(yè)的時(shí)間插件實(shí)例代碼

    使用vue寫(xiě)一個(gè)翻頁(yè)的時(shí)間插件實(shí)例代碼

    最近在做自己項(xiàng)目中遇到一個(gè)非常簡(jiǎn)單的功能,跟大家分享下,這篇文章主要給大家介紹了關(guān)于使用vue寫(xiě)一個(gè)翻頁(yè)的時(shí)間插件的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • VUE中使用Vue-resource完成交互

    VUE中使用Vue-resource完成交互

    本篇文章主要介紹了VUE中使用Vue-resource完成交互,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • vue3+ts+MicroApp實(shí)戰(zhàn)教程

    vue3+ts+MicroApp實(shí)戰(zhàn)教程

    這篇文章主要介紹了vue3+ts+MicroApp實(shí)戰(zhàn)教程,分別在主應(yīng)用項(xiàng)目(main)和子應(yīng)用(childrenOne,childrenTwo)項(xiàng)目中安裝microApp,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2022-10-10
  • 淺談vue-cli加載不到dev-server.js的解決辦法

    淺談vue-cli加載不到dev-server.js的解決辦法

    本篇文章主要介紹了淺談vue-cli加載不到dev-server.js的解決辦法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • 詳解Vue如何實(shí)現(xiàn)響應(yīng)式布局

    詳解Vue如何實(shí)現(xiàn)響應(yīng)式布局

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)響應(yīng)式布局的兩種方法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-12-12

最新評(píng)論

城市| 乌什县| 鄂伦春自治旗| 景洪市| 新民市| 舒兰市| 灵宝市| 乐昌市| 枞阳县| 阳城县| 辽中县| 镶黄旗| 苗栗县| 隆尧县| 花莲县| 郑州市| 永年县| 衡阳市| 汾阳市| 四川省| 崇礼县| 文山县| 凌海市| 叙永县| 盐山县| 湘潭县| 比如县| 赤壁市| 洛隆县| 临猗县| 张家港市| 奇台县| 新邵县| 墨竹工卡县| 济源市| 孟连| 克拉玛依市| 莒南县| 阿坝| 沂水县| 临桂县|