詳解VUE響應式原理
1、響應式原理基礎
響應式基本原理是基于Object.defineProperty(obj, prop, descriptor)
,?descriptor
里面可以定義get和set方法,可以在獲取屬性值事觸發(fā)get方法(可以收集依賴),設置屬性值時觸發(fā)set方法(更新依賴)。
擴展:上面是vue2.0的響應式基本原理,vue3.0的基本原理是Proxy,可以監(jiān)聽屬性的get和set方法,監(jiān)聽屬性的添加和刪除等等,比Object.defineProperty能力更強,但是不兼容IE11。
2、核心對象:Dep與Watcher
Dep
: vue在data里申明的每一個屬性都會生成一個Dep的實例對象,Dep.subs存儲著當該屬性變化時需要去更新的Watcher;
Watcher
: 有3種情況會生成Watcher的實例對象,分別為:
1.定義在computed里的計算屬性;
2.在watch里寫的監(jiān)聽函數;
3.組件的渲染Watcher;
3、收集依賴與更新依賴
3.1 收集依賴
將Watcher的實例對象w分發(fā)到它所依賴的屬性的Dep中,過程如下:
1.將Dep.target = 當前的Watcer 的實例對象w;
2.w執(zhí)行定義的函數(即在computed/watch寫的函數);
3.執(zhí)行函數的過程如果使用data里定義的屬性,則會觸發(fā)屬性的get方法,get方法中Dep實例對象dep會將Dep.target中存儲的w放入到dep.subs數組中,完成依賴收集。
說明:Dep.target為當前Watcer的實例對象
3.2 更新依賴
?當修改我們申明的某個屬性時,會觸發(fā)屬性的set方法,set方法會將dep.subs數組中收集的Watcher實例對象進行更新,即觸發(fā)我們定義在computed和watch里面的函數。
4、源碼調試
4.1 測試的頁面代碼
<template> <div> <div>a:<input v-model="a" /></div> <div>c:{{ c }}</div> <div>b:<input v-model="b" /></div> </div> </template> <script> export default { data: () => { return { a: '', b: '' } }, computed: { c() { return 'source from ' + this.a; } }, watch: { b() { console.log('b changed'); } } }; </script>
上面的代碼vue初始化后會生成如下幾個對象:
1、對象說明
屬性a和b對應的Dep實例對象(收集a、b改變需要更新的Watcher):depA
、depB
;
頁面渲染函數生成對應的Watcher實例對象updateWatcher
;
computed屬性c生成對應的Watcher實例對象:watcherC
;
watch監(jiān)聽屬性b生成對應的Watcher實例對象:watcherB
;
2、Dep與Watcher的關系
a、b變化頁面需要重新渲染,所以updateWatcher
存在于depA
和depB
的subs
中;
計算屬性c依賴屬性a的變化更更新,所以watcherC
存在于depA的subs中;
b的變化會觸發(fā)定義watch 里b的監(jiān)聽函數,所以watcherB
存在于depB的subs中;
3、最終的關系結果
最終屬性a收集的依賴 depA.subs = [?updateWatcher,? watcherC]
;
最終屬性b收集的依賴 depB.subs = [?updateWatcher,? watcherB]
;
4.2? 源碼調試
找到源碼文件:node_modules\vue\dist\vue.runtime.esm.js
;
主要涉及如下幾個函數:
1、收集依賴的入口函數:initState(頁面初始化時執(zhí)行);
初始化順序是先data-->computed-->watch:原因是computed依賴data, watch依賴data和watch,被依賴的需要先初始化。
2、初始化computed和watch時,生成Watcher實例化對象
先執(zhí)行Watcher.get函數,將Dep.target = 當前Watcher實例化對象
觸發(fā)收集依賴
執(zhí)行計算屬性里面的函數,如果訪問到data中的某個屬性時,會觸發(fā)data屬性的get方法,觸發(fā)依賴收集:
當修改這個屬性時會觸發(fā)set方法,會觸發(fā)更新dep.subs里面watcher對象
最終觸發(fā)Watcher的更新函數,將待更新的watcher放入隊列中:
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!