Vue Object 的變化偵測實(shí)現(xiàn)代碼
數(shù)據(jù)觀察
Vue 中的對象變化偵測是通過Object.definePorperty實(shí)現(xiàn)的,但是Object.definePorperty的方式有缺陷,比如不能直接代理整個(gè)對象,每次都要循環(huán)遍歷對象的所有屬性;尤大大說之后會(huì)使用 ES6 中的Proxy 重寫這個(gè)部分。這篇博客介紹的是 Object.definePorperty 實(shí)現(xiàn)的對象偵測。
我們來看下面這段代碼,定義一個(gè) defineReactive 函數(shù),使用 Object.definePorperty 遍歷對象對象屬性的時(shí)候,設(shè)置 get 和 set;當(dāng)對象屬性被讀取的時(shí)候觸發(fā) get,對象屬性被設(shè)置的時(shí)候觸發(fā) set。這樣就完成了對 data 的數(shù)據(jù)劫持,因?yàn)?Vue 的思想是響應(yīng)式的,我們還需要收集這些變化。
function defineReactive(data,key,val){
Object.definePorperty(data,key,{
enumerable: true,
configurable: true,
get:function(){
return val;
}
set :function (newVal){
if(val === newVal){return}
val = newVal;
}
})
}
依賴 收集
創(chuàng)建一個(gè) Dep 類,在 get 中收集依賴,在 set 中新增依賴
class Dep{
constructor(){
this.arr = []
}
addSub(sub){
this.arr.push(sub)
}
removeSub(sub){
remove(this.arr,sub)
}
depend(){
if(window.target){
this.addSub(window.target)
}
}
notify(){
const arrs = this.arr.slice();
for(let i = 0; i< arrs.lenth ;i ++){
arrs[i].update();
}
}
}
function defineReactive(data,key,val){
let dep = new Dep()
Object.definePorperty(data,key,{
enumerable: true,
configurable: true,
get:function(){
dep.depend(); // 收集依賴
return val;
}
set :function (newVal){
if(val === newVal){return}
val = newVal;
dep.notify(); // 新增依賴
}
})
}
Observer 和 Watcher
我們發(fā)現(xiàn) defineReactive 函數(shù)只能將某一個(gè)屬性轉(zhuǎn)換為 get/set 的形式,所以我們需要一個(gè)觀察者 Observer 用來幫助遞歸的偵測所有的 key
class Observer{
constructor(value){
this.value = value
}
if(!Array.isArry(value)){
this.walk(value)
}
walk(obj){
const keys = Object.keys(obj)
for(let i = 0; i < keys.length ;i++){
defineReactive(data,keys[i],obj[keys[i])
}
}
}
當(dāng)這些依賴收集完成之后,我們要通知誰呢?怎么樣能讓視圖知道有變化更新?我們需要實(shí)現(xiàn)一個(gè)訂閱者 Watcher,
每次觸發(fā) get 的時(shí)候都將 dep 指向自己,這樣就可以收集到依賴;
每次 set 的時(shí)候都循環(huán)調(diào)用 Watcher 的 update 方法。
class Watcher{
constructor(vm,exp,cb){
this.vm = vm;
this.cb = cb;
this.exp = exp;
this.value = this.get();
}
get(){
Dep.target = this; // 將當(dāng)前訂閱者指向自己
var value = this.vm[exp]; // 觸發(fā)getter,添加自己到屬性訂閱器中
Dep.target = null; // 添加完畢,重置
return value;
}
update(){
const oldVal = this.value;
this.value = this.get();
this.cb.call(this.vm,this.value,oldVal)
}
}
當(dāng) Vue 實(shí)例掛載好之后,模板都會(huì)綁定一個(gè) Watcher,誰的屬性發(fā)生變化了就會(huì)通知響應(yīng)的 Watcher,Watcher 再去通知編譯器 Compile 進(jìn)行視圖更新
偵測沒辦法監(jiān)聽到對象上屬性的新增和刪除
Vue 通過Object.definePorperty將對象的 key 轉(zhuǎn)化為 getter setter 的形式來進(jìn)行偵測,但是無法追蹤到屬性的新增和刪除,所以 Vue 中提供了 vm.get 來實(shí)現(xiàn)
到此這篇關(guān)于Vue Object 的變化偵測實(shí)現(xiàn)代碼的文章就介紹到這了,更多相關(guān)Vue Object 變化偵測 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Vue中實(shí)現(xiàn)拖拽功能的實(shí)例
Vue實(shí)現(xiàn)拖拽功能的基本原理是監(jiān)聽鼠標(biāo)事件,實(shí)時(shí)更新拖拽元素的位置,最后在合適的時(shí)機(jī)停止拖拽并更新元素位置,在Vue中,我們可以通過綁定相關(guān)事件來實(shí)現(xiàn)這一功能2025-02-02
vue中el-tree增加節(jié)點(diǎn)后如何重新刷新
這篇文章主要介紹了vue中el-tree增加節(jié)點(diǎn)后如何重新刷新,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
vue點(diǎn)擊按鈕實(shí)現(xiàn)讓頁面的某一個(gè)元素全屏展示
這篇文章主要介紹了vue點(diǎn)擊按鈕實(shí)現(xiàn)讓頁面的某一個(gè)元素全屏展示,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
vue-router 源碼之實(shí)現(xiàn)一個(gè)簡單的 vue-router
這篇文章主要介紹了vue-router 源碼之實(shí)現(xiàn)一個(gè)簡單的 vue-router,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
Vue 頁面狀態(tài)保持頁面間數(shù)據(jù)傳輸?shù)囊环N方法(推薦)
vue router給我們提供了兩種頁面間傳遞參數(shù)的方式,一種是動(dòng)態(tài)路由匹配,一種是編程式導(dǎo)航,接下來通過本文給大家介紹Vue 頁面狀態(tài)保持頁面間數(shù)據(jù)傳輸?shù)囊环N方法,需要的朋友可以參考下2018-11-11
vue3錨點(diǎn)定位兩種實(shí)現(xiàn)方式示例
這篇文章主要給大家介紹了關(guān)于vue3錨點(diǎn)定位兩種實(shí)現(xiàn)的相關(guān)資料,說到錨點(diǎn)定位,很多人第一時(shí)間會(huì)想到 a標(biāo)簽,但是a標(biāo)簽實(shí)現(xiàn)的錨點(diǎn)定位并不是那么的完美,需要的朋友可以參考下2023-07-07

