欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

導(dǎo) 讀

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供政和網(wǎng)站建設(shè)、政和做網(wǎng)站、政和網(wǎng)站設(shè)計、政和網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、政和企業(yè)網(wǎng)站模板建站服務(wù),10余年政和做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

vue3.0中,響應(yīng)式數(shù)據(jù)部分棄用了 Object.defineProperty ,使用 Proxy 來代替它。本文將主要通過以下方面來分析為什么vue選擇棄用 Object.defineProperty

  • Object.defineProperty 真的無法監(jiān)測數(shù)組下標(biāo)的變化嗎?
  • 分析vue2.x中對數(shù)組 Observe 部分源碼
  • 對比 Object.definePropertyProxy

一、無法監(jiān)控到數(shù)組下標(biāo)的變化?

在一些技術(shù)博客上看到過這樣一種說法,認(rèn)為 Object.defineProperty 有一個缺陷是無法監(jiān)聽數(shù)組變化:

無法監(jiān)控到數(shù)組下標(biāo)的變化,導(dǎo)致直接通過數(shù)組的下標(biāo)給數(shù)組設(shè)置值,不能實(shí)時響應(yīng)。所以vue才設(shè)置了7個變異數(shù)組( push 、 pop 、 shift 、 unshift 、 splice 、 sort 、 reverse )的 hack 方法來解決問題。

Object.defineProperty 的第一個缺陷,無法監(jiān)聽數(shù)組變化。 然而Vue的文檔提到了Vue是可以檢測到數(shù)組變化的,但是只有以下八種方法, vm.items[indexOfItem] = newValue 這種是無法檢測的。

這種說法是有問題的,事實(shí)上, Object.defineProperty 本身是可以監(jiān)控到數(shù)組下標(biāo)的變化的,只是在 Vue 的實(shí)現(xiàn)中,從性能/體驗的性價比考慮,放棄了這個特性。

下面我們通過一個例子來為 Object.defineProperty 正名:

function defineReactive(data, key, value) {
 Object.defineProperty(data, key, {
 enumerable: true,
 configurable: true,
 get: function defineGet() {
 console.log(`get key: ${key} value: ${value}`)
 return value
 },
 set: function defineSet(newVal) {
 console.log(`set key: ${key} value: ${newVal}`)
 value = newVal
 }
 })
}

function observe(data) {
 Object.keys(data).forEach(function(key) {
 defineReactive(data, key, data[key])
 })
}

let arr = [1, 2, 3]
observe(arr)

上面代碼對數(shù)組arr的每個屬性通過 Object.defineProperty 進(jìn)行劫持,下面我們對數(shù)組arr進(jìn)行操作,看看哪些行為會觸發(fā)數(shù)組的 gettersetter 方法。

1. 通過下標(biāo)獲取某個元素和修改某個元素的值

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

可以看到,通過下標(biāo)獲取某個元素會觸發(fā) getter 方法, 設(shè)置某個值會觸發(fā) setter

方法。

接下來,我們再試一下數(shù)組的一些操作方法,看看是否會觸發(fā)。

2. 數(shù)組的 push 方法

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

push 并未觸發(fā) settergetter 方法,數(shù)組的下標(biāo)可以看做是對象中的 key ,這里 push 之后相當(dāng)于增加了下索引為3的元素,但是并未對新的下標(biāo)進(jìn)行 observe ,所以不會觸發(fā)。

3. 數(shù)組的 unshift 方法

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

我擦,發(fā)生了什么?

unshift 操作會導(dǎo)致原來索引為0,1,2,3的值發(fā)生變化,這就需要將原來索引為0,1,2,3的值取出來,然后重新賦值,所以取值的過程觸發(fā)了 getter ,賦值時觸發(fā)了 setter 。

下面我們嘗試通過索引獲取一下對應(yīng)的元素:

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

只有索引為0,1,2的屬性才會觸發(fā) getter 。

這里我們可以對比對象來看,arr數(shù)組初始值為[1, 2, 3],即只對索引為0,1,2執(zhí)行了 observe 方法,所以無論后來數(shù)組的長度發(fā)生怎樣的變化,依然只有索引為0,1,2的元素發(fā)生變化才會觸發(fā),其他的新增索引,就相當(dāng)于對象中新增的屬性,需要再手動 observe 才可以。

4. 數(shù)組的 pop 方法

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

當(dāng)移除的元素為引用為2的元素時,會觸發(fā) getter

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

刪除了索引為2的元素后,再去修改或獲取它的值時,不會再觸發(fā) settergetter

這和對象的處理是同樣的,數(shù)組的索引被刪除后,就相當(dāng)于對象的屬性被刪除一樣,不會再去觸發(fā) observe 。

到這里,我們可以簡單的總結(jié)一下結(jié)論。

Object.defineProperty 在數(shù)組中的表現(xiàn)和在對象中的表現(xiàn)是一致的,數(shù)組的索引就可以看做是對象中的 key 。

  • 通過索引訪問或設(shè)置對應(yīng)元素的值時,可以觸發(fā) gettersetter 方法
  • 通過 pushunshift 會增加索引,對于新增加的屬性,需要再手動初始化才能被 observe 。
  • 通過 popshift 刪除元素,會刪除并更新索引,也會觸發(fā) settergetter 方法。

所以, Object.defineProperty 是有監(jiān)控數(shù)組下標(biāo)變化的能力的,只是vue2.x放棄了這個特性。

二、vue對數(shù)組的observe做了哪些處理?

vue的 Observer 類定義在 core/observer/index.js 中。

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

可以看到,vue的 Observer 對數(shù)組做了單獨(dú)的處理。

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

hasProto 是判斷數(shù)組的實(shí)例是否有 __proto__ 屬性,如果有 __proto__ 屬性就會執(zhí)行 protoAugment 方法,將 arrayMethods 重寫到原型上。 hasProto 定義如下。

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

arrayMethods 是對數(shù)組的方法進(jìn)行重寫,定義在 core/observer/array.js 中, 下面是這部分源碼的分析。

/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'

// 復(fù)制數(shù)組構(gòu)造函數(shù)的原型,Array.prototype也是一個數(shù)組。
const arrayProto = Array.prototype
// 創(chuàng)建對象,對象的__proto__指向arrayProto,所以arrayMethods的__proto__包含數(shù)組的所有方法。
export const arrayMethods = Object.create(arrayProto)

// 下面的數(shù)組是要進(jìn)行重寫的方法
const methodsToPatch = [
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
// 遍歷methodsToPatch數(shù)組,對其中的方法進(jìn)行重寫
methodsToPatch.forEach(function (method) {
 // cache original method
 const original = arrayProto[method]
 // def方法定義在lang.js文件中,是通過object.defineProperty對屬性進(jìn)行重新定義。
 // 即在arrayMethods中找到我們要重寫的方法,對其進(jìn)行重新定義
 def(arrayMethods, method, function mutator (...args) {
 const result = original.apply(this, args)
 const ob = this.__ob__
 let inserted
 switch (method) {
 // 上面已經(jīng)分析過,對于push,unshift會新增索引,所以需要手動observe
 case 'push':
 case 'unshift':
 inserted = args
 break
 // splice方法,如果傳入了第三個參數(shù),也會有新增索引,所以也需要手動observe
 case 'splice':
 inserted = args.slice(2)
 break
 }
 // push,unshift,splice三個方法觸發(fā)后,在這里手動observe,其他方法的變更會在當(dāng)前的索引上進(jìn)行更新,所以不需要再執(zhí)行ob.observeArray
 if (inserted) ob.observeArray(inserted)
 // notify change
 ob.dep.notify()
 return result
 })
})

三 Object.defineProperty VS Proxy

上面已經(jīng)知道 Object.defineProperty 對數(shù)組和對象的表現(xiàn)是一致的,那么它和 Proxy 對比存在哪些優(yōu)缺點(diǎn)呢?

1. Object.defineProperty只能劫持對象的屬性,而Proxy是直接代理對象。

由于 Object.defineProperty 只能對屬性進(jìn)行劫持,需要遍歷對象的每個屬性,如果屬性值也是對象,則需要深度遍歷。而 Proxy 直接代理對象,不需要遍歷操作。

2. Object.defineProperty對新增屬性需要手動進(jìn)行Observe。

由于 Object.defineProperty 劫持的是對象的屬性,所以新增屬性時,需要重新遍歷對象,對其新增屬性再使用 Object.defineProperty 進(jìn)行劫持。

也正是因為這個原因,使用vue給 data 中的數(shù)組或?qū)ο笮略鰧傩詴r,需要使用 vm.$set 才能保證新增的屬性也是響應(yīng)式的。

下面看一下vue的 set 方法是如何實(shí)現(xiàn)的, set 方法定義在 core/observer/index.js ,下面是核心代碼。

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export function set (target: Array<any> | Object, key: any, val: any): any {
 // 如果target是數(shù)組,且key是有效的數(shù)組索引,會調(diào)用數(shù)組的splice方法,
 // 我們上面說過,數(shù)組的splice方法會被重寫,重寫的方法中會手動Observe
 // 所以vue的set方法,對于數(shù)組,就是直接調(diào)用重寫splice方法
 if (Array.isArray(target) && isValidArrayIndex(key)) {
 target.length = Math.max(target.length, key)
 target.splice(key, 1, val)
 return val
 }
 // 對于對象,如果key本來就是對象中的屬性,直接修改值就可以觸發(fā)更新
 if (key in target && !(key in Object.prototype)) {
 target[key] = val
 return val
 }
 // vue的響應(yīng)式對象中都會添加了__ob__屬性,所以可以根據(jù)是否有__ob__屬性判斷是否為響應(yīng)式對象
 const ob = (target: any).__ob__
 // 如果不是響應(yīng)式對象,直接賦值
 if (!ob) {
 target[key] = val
 return val
 }
 // 調(diào)用defineReactive給數(shù)據(jù)添加了 getter 和 setter,
 // 所以vue的set方法,對于響應(yīng)式的對象,就會調(diào)用defineReactive重新定義響應(yīng)式對象,defineReactive 函數(shù)
 defineReactive(ob.value, key, val)
 ob.dep.notify()
 return val
}

set 方法中,對 target 是數(shù)組和對象做了分別的處理, target 是數(shù)組時,會調(diào)用重寫過的 splice 方法進(jìn)行手動 Observe 。

對于對象,如果 key 本來就是對象的屬性,則直接修改值觸發(fā)更新,否則調(diào)用 defineReactive 方法重新定義響應(yīng)式對象。

如果采用 proxy 實(shí)現(xiàn), Proxy 通過 set(target, propKey, value, receiver) 攔截對象屬性的設(shè)置,是可以攔截到對象的新增屬性的。

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

不止如此, Proxy 對數(shù)組的方法也可以監(jiān)測到,不需要像上面vue2.x源碼中那樣進(jìn)行 hack

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

完美?。?!

3. Proxy支持13種攔截操作,這是defineProperty所不具有的

get(target, propKey, receiver):攔截對象屬性的讀取,比如 proxy.fooproxy['foo'] 。

set(target, propKey, value, receiver):攔截對象屬性的設(shè)置,比如 proxy.foo = vproxy['foo'] = v ,返回一個布爾值。

has(target, propKey):攔截 propKey in proxy 的操作,返回一個布爾值。

deleteProperty(target, propKey):攔截 delete proxy[propKey] 的操作,返回一個布爾值。

ownKeys(target):攔截 Object.getOwnPropertyNames(proxy) 、 Object.getOwnPropertySymbols(proxy)Object.keys(proxy) 、 for...in 循環(huán),返回一個數(shù)組。該方法返回目標(biāo)對象所有自身的屬性的屬性名,而 Object.keys() 的返回結(jié)果僅包括目標(biāo)對象自身的可遍歷屬性。

getOwnPropertyDescriptor(target, propKey):攔截 Object.getOwnPropertyDescriptor(proxy, propKey) ,返回屬性的描述對象。

defineProperty(target, propKey, propDesc):攔截 Object.defineProperty(proxy, propKey, propDesc) 、 Object.defineProperties(proxy, propDescs) ,返回一個布爾值。

preventExtensions(target):攔截 Object.preventExtensions(proxy) ,返回一個布爾值。

getPrototypeOf(target):攔截 Object.getPrototypeOf(proxy) ,返回一個對象。

isExtensible(target):攔截 Object.isExtensible(proxy) ,返回一個布爾值。

setPrototypeOf(target, proto):攔截 Object.setPrototypeOf(proxy, proto) ,返回一個布爾值。如果目標(biāo)對象是函數(shù),那么還有兩種額外操作可以攔截。

apply(target, object, args):攔截 Proxy 實(shí)例作為函數(shù)調(diào)用的操作,比如 proxy(...args)proxy.call(object, ...args) 、 proxy.apply(...) 。

construct(target, args):攔截 Proxy 實(shí)例作為構(gòu)造函數(shù)調(diào)用的操作,比如 new proxy(...args)

4. 新標(biāo)準(zhǔn)性能紅利

Proxy 作為新標(biāo)準(zhǔn),長遠(yuǎn)來看,JS引擎會繼續(xù)優(yōu)化 Proxy ,但 gettersetter 基本不會再有針對性優(yōu)化。

5. Proxy兼容性差

為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)

可以看到, Proxy 對于IE瀏覽器來說簡直是災(zāi)難。

并且目前并沒有一個完整支持 Proxy 所有攔截方法的Polyfill方案,有一個google編寫的proxy-polyfill 也只支持了 get,set,apply,construct 四種攔截,可以支持到IE9+和Safari 6+。

四 總結(jié)

  • Object.defineProperty 對數(shù)組和對象的表現(xiàn)一直,并非不能監(jiān)控數(shù)組下標(biāo)的變化,vue2.x中無法通過數(shù)組索引來實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的自動更新是vue本身的設(shè)計導(dǎo)致的,不是 defineProperty 的鍋。
  • Object.defineProperty 和 Proxy 本質(zhì)差別是,defineProperty 只能對屬性進(jìn)行劫持,所以出現(xiàn)了需要遞歸遍歷,新增屬性需要手動 Observe 的問題。
  • Proxy 作為新標(biāo)準(zhǔn),瀏覽器廠商勢必會對其進(jìn)行持續(xù)優(yōu)化,但它的兼容性也是塊硬傷,并且目前還沒有完整的polifill方案。

參考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

https://www.jb51.net/article/171872.htm

https://zhuanlan.zhihu.com/p/35080324

http://es6.ruanyifeng.com/#docs/proxy

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

標(biāo)題名稱:為什么Vue3.0使用Proxy實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽(defineProperty表示不背這個鍋)
標(biāo)題網(wǎng)址:http://aaarwkj.com/article30/gjoiso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、云服務(wù)器、建站公司、定制開發(fā)微信公眾號、小程序開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營
亚洲欧洲久久激情久av| 亚洲伊人成综合人影院| 亚洲高清精品一区二区| 国产91在线精品超碰人人| 国语对白视频在线观看| 国产精品久久久久久久亚洲| 亚洲综合激情另类专区| 国产精品蜜臀av在线一区| 日韩av中文一区二区| 欧美日韩精品亚洲成人精品| 国产成人精品视频午夜蜜蜂| 高清欧美一区二区三区日本| 午夜福利中文字幕在线亚洲| 少妇高潮试看二十分钟| 91午夜福利视频免费播放| 久久精品国产亚洲av高清综合| 日本一级黄色影视大全| 色呦呦视频在线免费观看| 午夜18禁毛片免费看| 国产精品va在线观看入口| 啊啊舒服爽用力爱我视频| 精品国产免费第一区二区三| 日韩中字伦理熟妇人妻| 神马免费午夜福利剧场| 一区二区在线日韩欧美| 91色九色吧在线观看| 国产日韩精品一区二区三区在线| 日韩人妻av免费电影| 亚洲丰满老熟女激情av| 亚洲精品一品区二品区三区 | 亚洲精品主播一区二区三区| 久久青青草原一区二区| 嫩草网站国产精品一区二| 亚洲黄色av网站在线| 国产偷自一区二区三区| 中文字幕日本人妻少妇| 亚洲最新精品一区二区| 色哟哟国产精品一区自拍| 日韩精品一区二区三区四区在线视频 | 青青成线在人线免费啪| 久久亚洲综合精品少妇|