您好,登錄后才能下訂單哦!
這篇“vue處理響應式數據的方法是什么”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“vue處理響應式數據的方法是什么”文章吧。
那繼續下一個問題。
這個回答感覺就比較官方了,沒有回答到我想要的答案。
理由其實很簡單,因為proxy方法不能代理值類型,只能代理對象。所以需要一個額外的方法,來處理值類型的數據。
當然你也可以一個ref走天下,vue源碼做了兼容會自動進行轉換
有了這些理解之后,我們來看今天的重頭戲,自己仿照vue3源碼,實現一個響應式系統。
相關代碼可以點擊查看,根據文章的標題選擇不同的分支即可。
在前文我們也介紹了,reactive其實用的是proxy代理對象。
我們可以使用proxy實現一個簡單的代理函數reactive。
function reactive(target) { const isObject = (val) => val !== null && typeof val === 'object'
if (!isObject(target)) { console.warn(`數據必須是對象: ${String(target)}`) return target
} const proxy = new Proxy(target, { get: (target, key, receiver) => { console.log('get', key) const res = Reflect.get(target, key, receiver) // track(target, key)
// 這句很關鍵,不然嵌套數據,里面的不會響應
if (isObject(res)) { return reactive(res)
} return res
}, set: (target, key, value, receiver) => { console.log('set', key, value) const result = Reflect.set(target, key, value, receiver) // trigger(target, key, value)
return result
}, deleteProperty: () => { const result = Reflect.deleteProperty(target, key) return result
}
}) return proxy
} const person = reactive({ name: '', age: 18, like: ['apple']
})
person.name = 'vue test'復制代碼
注意:Reflect
Reflect.get(target, key)跟直接訪問target[key]是有一點差別的。
當代理對象中,有get,set等this指向的時候,可以重定向this。
舉個例子:
const person = new Proxy({ name: "vue test", age: 18, get info() { return 'name is :' + this.name + ' age is:' + this.age
}
}, { get: (target, key, receiver) => { console.log('get', key) // return target[key]
return Reflect.get(target, key, receiver)
}
}) console.log(person.info)復制代碼
使用Reflect,我們在訪問name, age的時候均可以觸發。
改成target之后,則只會在info觸發一次。
return target[key]復制代碼
在vue3中,ref是通過get和set來實現的。
跟上面類似,也還是先聲明一個函數,然后通過get,set來訪問數據。
function ref(value) { return new RefImpl(value)
} class RefImpl { constructor(value) { // 如果值是對象,則用reactive處理
this._value = isObject(value) ? reactive(value) : value // 記錄一下初始值
this._rawValue = value
} get value() { // trackRefValue(this)
return this._value
} set value(newVal) { if (!Object.is(newVal, this._rawValue)) { // 更新原始數據
this._rawValue = newVal // 更新 .value 的值
this._value = isObject(newVal) ? reactive(newVal) : value // triggerRefValue(this)
}
}
}復制代碼
源碼也很直觀的解釋了,為什么ref的使用一定要用.value的方式使用,因為get/set就是通過value來設置的。
數據代理我們已經完成了,但是數據更改之后,怎么通知組件實現雙向綁定呢?
答案就是依賴收集與觸發,也就是當觸發get的時候,我把觸發get的這個條件【函數】給保存起來,當觸發set的時候,重新執行一下觸發一下這個條件【函數】不就行了嗎。
還是來看代碼,我加一個track收集的方法,加一個target觸發的方法。(也就是上面代碼片段注釋掉的兩行代碼)
除此之外,還需要一個effect函數來管理觸發函數。
let activeEffect = null
const targetMap = new WeakMap() // 依賴收集/觸發
function track(target, key) { let depsMap = targetMap.get(target) if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
} let dep = depsMap.get(key) if (!dep) {
dep = new Set()
}
dep.add(activeEffect)
depsMap.set(key, dep)
} function trigger(target, key, value) { const depsMap = targetMap.get(target) if (!depsMap) { return
} const deps = depsMap.get(key) if (!deps) { return
}
deps.forEach(effectFn => { if (effectFn.scheduler) {
effectFn.scheduler()
} else { effectFn()
}
})
} function effect(fn,options = {}) { const effectFn = () => { try {
activeEffect = effectFn return fn()
} catch (error) {
activeEffect = null
}
} if (!options.lazy) { effectFn()
}
effectFn.scheduler = options.scheduler
return effectFn
} const person = reactive({ name: "hello world"
}) effect(() => { console.log('effect person', person.name)
}) setTimeout(() => {
person.name = 'setTimeout world'
}, 2000)復制代碼
activeEffect用來存觸發的條件函數。
targetMap用來存放依賴字典,格式如下
{
target: {
key: []
}
}復制代碼
輸出結果為hello world,2秒之后,重新執行依賴性函數,輸出setTimeout world
數據的代理并不復雜,就是在proxy的基礎上,加上一些邊界處理。而要實現響應式則需要加上依賴收集,依賴觸發的實現。
effect是一個很重要的函數,很多api是基于這個函數開發的,比如useEffect, watch。組件的更新也是基于effect函數,這個后續還會提到。
如果不太理解effect,可以梳理一下執行順序。
1.調用effect函數,并傳入參數fn
2.申明一個effectFn函數,并執行,將函數保存為activeEffect
3.執行fn,讀取person.name
4.走proxy的get代理
5.收集依賴,將activeEffect保存的函數存到全局map中
6.此時,effect函數執行完畢,等待數據更新
7.2s后,走proxy的set代理
8.執行全局map中保存的函數,重新執行effect函數,再次回到步驟1
相關代碼可以點擊查看,選擇lesson3分支即可。
文章相關代碼,可在vue/examples中查看,仿vue實現版本可在packages/reactivity中查看。
以上就是關于“vue處理響應式數據的方法是什么”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。