新聞中心
現(xiàn)在是時(shí)候深入了!Vue 最獨(dú)特的特性之一,是其非侵入性的響應(yīng)性系統(tǒng)。數(shù)據(jù)模型是被代理的 JavaScript 對(duì)象。而當(dāng)你修改它們時(shí),視圖會(huì)進(jìn)行更新。這讓狀態(tài)管理非常簡(jiǎn)單直觀,不過理解其工作原理同樣重要,這樣你可以避開一些常見的問題。在這個(gè)章節(jié),我們將研究一下 Vue 響應(yīng)性系統(tǒng)的底層的細(xì)節(jié)。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、小程序定制開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了貴港免費(fèi)建站歡迎大家使用!
在 Vue Mastery 上免費(fèi)觀看關(guān)于深入響應(yīng)性原理的視頻。
#什么是響應(yīng)性
這個(gè)術(shù)語(yǔ)在程序設(shè)計(jì)中經(jīng)常被提及,但這是什么意思呢?響應(yīng)性是一種允許我們以聲明式的方式去適應(yīng)變化的一種編程范例。人們通常展示的典型例子,是一份 excel 電子表格 (一個(gè)非常好的例子)。
點(diǎn)擊此處看視頻
如果將數(shù)字 2 放在第一個(gè)單元格中,將數(shù)字 3 放在第二個(gè)單元格中并要求提供 SUM,則電子表格會(huì)將其計(jì)算出來給你。不要驚奇,同時(shí),如果你更新第一個(gè)數(shù)字,SUM 也會(huì)自動(dòng)更新。
JavaScript 通常不是這樣工作的——如果我們想用 JavaScript 編寫類似的內(nèi)容:
var val1 = 2
var val2 = 3
var sum = val1 + val2
// sum
// 5
val1 = 3
// sum
// 5如果我們更新第一個(gè)值,sum 不會(huì)被修改。
那么我們?nèi)绾斡?JavaScript 實(shí)現(xiàn)這一點(diǎn)呢?
- 檢測(cè)其中某一個(gè)值是否發(fā)生變化
- 用跟蹤 (track) 函數(shù)修改值
- 用觸發(fā) (trigger) 函數(shù)更新為最新的值
#Vue 如何追蹤變化?
當(dāng)把一個(gè)普通的 JavaScript 對(duì)象作為 data 選項(xiàng)傳給應(yīng)用或組件實(shí)例的時(shí)候,Vue 會(huì)使用帶有 getter 和 setter 的處理程序遍歷其所有 property 并將其轉(zhuǎn)換為 Proxy。這是 ES6 僅有的特性,但是我們?cè)?Vue 3 版本也使用了 Object.defineProperty 來支持 IE 瀏覽器。兩者具有相同的 Surface API,但是 Proxy 版本更精簡(jiǎn),同時(shí)提升了性能。
點(diǎn)擊此處實(shí)現(xiàn)
該部分需要稍微地了解下 Proxy 的某些知識(shí)!所以,讓我們深入了解一下。關(guān)于 Proxy 的文獻(xiàn)很多,但是你真正需要知道的是 Proxy 是一個(gè)包含另一個(gè)對(duì)象或函數(shù)并允許你對(duì)其進(jìn)行攔截的對(duì)象。
我們是這樣使用它的:new Proxy(target, handler)
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, prop) {
return target[prop]
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos好的,到目前為止,我們只是包裝這個(gè)對(duì)象并返回它。很酷,但還不是那么有用。請(qǐng)注意,我們把對(duì)象包裝在 Proxy 里的同時(shí)可以對(duì)其進(jìn)行攔截。這種攔截被稱為陷阱。
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, prop) {
console.log('intercepted!')
return target[prop]
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos除了控制臺(tái)日志,我們可以在這里做任何我們想做的事情。如果我們?cè)敢猓覀兩踔量梢圆环祷貙?shí)際值。這就是為什么 Proxy 對(duì)于創(chuàng)建 API 如此強(qiáng)大。
此外,Proxy 還提供了另一個(gè)特性。我們不必像這樣返回值:target[prop],而是可以進(jìn)一步使用一個(gè)名為 Reflect 的方法,它允許我們正確地執(zhí)行 this 綁定,就像這樣:
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, prop, receiver) {
return Reflect.get(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos 我們之前提到過,為了有一個(gè) API 能夠在某些內(nèi)容發(fā)生變化時(shí)更新最終值,我們必須在內(nèi)容發(fā)生變化時(shí)設(shè)置新的值。我們?cè)谔幚砥?,一個(gè)名為 track 的函數(shù)中執(zhí)行此操作,該函數(shù)可以傳入 target 和 key 兩個(gè)參數(shù)。
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, prop, receiver) {
track(target, prop)
return Reflect.get(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos最后,當(dāng)某些內(nèi)容發(fā)生改變時(shí)我們會(huì)設(shè)置新的值。為此,我們將通過觸發(fā)這些更改來設(shè)置新 Proxy 的更改:
const dinner = {
meal: 'tacos'
}
const handler = {
get(target, prop, receiver) {
track(target, prop)
return Reflect.get(...arguments)
},
set(target, key, value, receiver) {
trigger(target, key)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
// tacos還記得幾段前的列表嗎?現(xiàn)在我們有了一些關(guān)于 Vue 如何處理這些更改的答案:
- 當(dāng)某個(gè)值發(fā)生變化時(shí)進(jìn)行檢測(cè):我們不再需要這樣做,因?yàn)?Proxy 允許我們攔截它
- 跟蹤更改它的函數(shù):我們?cè)?Proxy 中的 getter 中執(zhí)行此操作,稱為
effect - 觸發(fā)函數(shù)以便它可以更新最終值:我們?cè)?Proxy 中的 setter 中進(jìn)行該操作,名為
trigger
Proxy 對(duì)象對(duì)于用戶來說是不可見的,但是在內(nèi)部,它們使 Vue 能夠在 property 的值被訪問或修改的情況下進(jìn)行依賴跟蹤和變更通知。從 Vue 3 開始,我們的響應(yīng)性現(xiàn)在可以在獨(dú)立的包中使用。需要注意的是,記錄轉(zhuǎn)換后的數(shù)據(jù)對(duì)象時(shí),瀏覽器控制臺(tái)輸出的格式會(huì)有所不同,因此你可能需要安裝 vue-devtools,以提供一種更易于檢查的界面。
#Proxy 對(duì)象
Vue 在內(nèi)部跟蹤所有已被設(shè)置為響應(yīng)式的對(duì)象,因此它始終會(huì)返回同一個(gè)對(duì)象的 Proxy 版本。
從響應(yīng)式 Proxy 訪問嵌套對(duì)象時(shí),該對(duì)象在返回之前也被轉(zhuǎn)換為 Proxy:
const handler = {
get(target, prop, receiver) {
track(target, prop)
const value = Reflect.get(...arguments)
if (isObject(value)) {
return reactive(value)
} else {
return value
}
}
// ...
}#Proxy vs 原始標(biāo)識(shí)
Proxy 的使用確實(shí)引入了一個(gè)需要注意的新警告:在身份比較方面,被代理對(duì)象與原始對(duì)象不相等 (===)。例如:
const obj = {}
const wrapped = new Proxy(obj, handlers)
console.log(obj === wrapped) // false在大多數(shù)情況下,原始版本和包裝版本的行為相同,但請(qǐng)注意,它們?cè)谝蕾噰?yán)格比對(duì)的操作下將是失敗的,例如 .filter() 或 .map()。使用選項(xiàng)式 API 時(shí),這種警告不太可能出現(xiàn),因?yàn)樗许憫?yīng)式都是從 this 訪問的,并保證已經(jīng)是 Proxy。
但是,當(dāng)使用合成 API 顯式創(chuàng)建響應(yīng)式對(duì)象時(shí),最佳做法是不要保留對(duì)原始對(duì)象的引用,而只使用響應(yīng)式版本:
const obj = reactive({
count: 0
}) // no reference to original#偵聽器
每個(gè)組件實(shí)例都有一個(gè)相應(yīng)的偵聽器實(shí)例,該實(shí)例將在組件渲染期間把“觸碰”的所有 property 記錄為依賴項(xiàng)。之后,當(dāng)觸發(fā)依賴項(xiàng)的 setter 時(shí),它會(huì)通知偵聽器,從而使得組件重新渲染。
點(diǎn)擊此處實(shí)現(xiàn)
將對(duì)象作為數(shù)據(jù)傳遞給組件實(shí)例時(shí),Vue 會(huì)將其轉(zhuǎn)換為 Proxy。這個(gè) Proxy 使 Vue 能夠在 property 被訪問或修改時(shí)執(zhí)行依賴項(xiàng)跟蹤和更改通知。每個(gè) property 都被視為一個(gè)依賴項(xiàng)。
首次渲染后,組件將跟蹤一組依賴列表——即在渲染過程中被訪問的 property。反過來,組件就成為了其每個(gè) property 的訂閱者。當(dāng) Proxy 攔截到 set 操作時(shí),該 property 將通知其所有訂閱的組件重新渲染。
如果你使用的是 Vue2.x 及以下版本,你可能會(huì)對(duì)這些版本中存在的一些更改檢測(cè)警告感興趣,在這里進(jìn)行更詳細(xì)的探討。
分享標(biāo)題:創(chuàng)新互聯(lián)VUE3教程:Vue 3.0 響應(yīng)性 深入響應(yīng)性原理
URL標(biāo)題:http://m.fisionsoft.com.cn/article/ccdcejc.html


咨詢
建站咨詢
