亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時間:2019年10月14日 10:03:08   作者:于是乎_  
這篇文章主要介紹了為什么Vue3.0使用Proxy實現(xiàn)數(shù)據(jù)監(jiān)聽?defineProperty表示不背這個鍋,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

導(dǎo) 讀

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è)置值,不能實時響應(yīng)。所以vue才設(shè)置了7個變異數(shù)組( pushpop 、 shift 、 unshiftsplice 、 sort 、 reverse )的 hack 方法來解決問題。

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

這種說法是有問題的,事實上, Object.defineProperty 本身是可以監(jiān)控到數(shù)組下標(biāo)的變化的,只是在 Vue 的實現(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 進行劫持,下面我們對數(shù)組arr進行操作,看看哪些行為會觸發(fā)數(shù)組的 gettersetter 方法。

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

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

方法。

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

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

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

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

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

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

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

只有索引為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 方法

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

刪除了索引為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 中。

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

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

arrayMethods 是對數(shù)組的方法進行重寫,定義在 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ù)組是要進行重寫的方法
const methodsToPatch = [
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
// 遍歷methodsToPatch數(shù)組,對其中的方法進行重寫
methodsToPatch.forEach(function (method) {
 // cache original method
 const original = arrayProto[method]
 // def方法定義在lang.js文件中,是通過object.defineProperty對屬性進行重新定義。
 // 即在arrayMethods中找到我們要重寫的方法,對其進行重新定義
 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)前的索引上進行更新,所以不需要再執(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)缺點呢?

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

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

2. Object.defineProperty對新增屬性需要手動進行Observe。

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

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

下面看一下vue的 set 方法是如何實現(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 方法進行手動 Observe 。

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

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

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

完美?。?!

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ù)調(diào)用的操作,比如 proxy(...args) 、 proxy.call(object, ...args) 、 proxy.apply(...)

construct(target, args):攔截 Proxy 實例作為構(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兼容性差

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

參考

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

http://chabaoo.cn/article/171872.htm

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 打包組件報錯:Error:Cannot?find?module?'vue/compiler-sfc'

    打包組件報錯:Error:Cannot?find?module?'vue/compiler-sfc&ap

    最近遇到這樣的問題,vue組件庫搭建過程中使用webpack打包組件時報錯,本文給大家分享打包組件報錯:Error:?Cannot?find?module?‘vue/compiler-sfc‘的解決方法,感興趣的朋友一起看看吧
    2023-12-12
  • vue中關(guān)于trigger的用法

    vue中關(guān)于trigger的用法

    這篇文章主要介紹了vue中關(guān)于trigger的用法說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 在vue-cli項目中如何使用swiper

    在vue-cli項目中如何使用swiper

    這篇文章主要介紹了在vue-cli項目中如何使用swiper問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Vue.js路由實現(xiàn)選項卡簡單實例

    Vue.js路由實現(xiàn)選項卡簡單實例

    這篇文章主要為大家詳細(xì)介紹了Vue.js路由實現(xiàn)選項卡簡單實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • vue 使用vue-i18n做全局中英文切換的方法

    vue 使用vue-i18n做全局中英文切換的方法

    這篇文章主要介紹了vue 使用vue-i18n做全局中英文切換的實現(xiàn)方法,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2018-10-10
  • vue關(guān)于data變量定義的問題

    vue關(guān)于data變量定義的問題

    這篇文章主要介紹了vue關(guān)于data變量定義的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • vue監(jiān)聽頁面滾動到某個高度觸發(fā)事件流程

    vue監(jiān)聽頁面滾動到某個高度觸發(fā)事件流程

    這篇文章主要介紹了vue監(jiān)聽頁面滾動到某個高度觸發(fā)事件流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue element-ui el-table組件自定義合計(summary-method)的坑

    vue element-ui el-table組件自定義合計(summary-method)的坑

    這篇文章主要介紹了vue element-ui el-table組件自定義合計(summary-method)的坑及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 利用VUE框架,實現(xiàn)列表分頁功能示例代碼

    利用VUE框架,實現(xiàn)列表分頁功能示例代碼

    本篇文章主要介紹了利用VUE框架,實現(xiàn)列表分頁功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-01-01
  • vue實現(xiàn)動態(tài)進度條效果

    vue實現(xiàn)動態(tài)進度條效果

    這篇文章主要為大家詳細(xì)介紹了vue實現(xiàn)動態(tài)進度條效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評論