Vue3.0「十七」-- vue3.0升級(jí)新特性及Proxy重寫響應(yīng)式講解

vue3.0 升級(jí)內(nèi)容

全部用TS重寫的(響應(yīng)式锰霜、vdom、模本編譯)
性能提升桐早,減少代碼量
會(huì)調(diào)整部分API
Proxy重寫響應(yīng)式

vue2.x 馬上要過時(shí)了嗎

vue3.0從正式發(fā)布到推廣癣缅,還需要一段時(shí)間
vue2.x應(yīng)用范圍廣,有大量項(xiàng)目需要維護(hù)升級(jí)
proxy存在兼容性問題哄酝,且不能ployfill

社區(qū)熱門知識(shí)點(diǎn):Proxy重寫響應(yīng)式講解

回顧vue2.*的響應(yīng)式原理 [object.defindeProperty]

object.defindeProperty缺點(diǎn):

  • 深度監(jiān)聽需要一次性遞歸
  • 無法監(jiān)聽新增屬性/刪除屬性(vue.set/vue.delete)
  • 無法原生監(jiān)聽數(shù)組友存,需要特殊處理

vue3_Proxy實(shí)現(xiàn)響應(yīng)式原理

前置知識(shí)

Proxy ES6語法 對(duì)象用于定義基本操作的自定義行為(如屬性查找、賦值陶衅、枚舉屡立、函數(shù)調(diào)用等)。
Proxy 可以理解成搀军, 在目標(biāo)對(duì)象之前架設(shè)一層“ 攔截”膨俐, 外界對(duì)該對(duì)象的訪問, 都必須先通過這層攔截罩句, 因此提供了一種機(jī)制焚刺, 可以對(duì)外界的訪問進(jìn)行過濾和改寫。

語法:const p = new Proxy(target, handler)
target 要使用 Proxy 包裝的目標(biāo)對(duì)象(可以是任何類型的對(duì)象门烂,包括原生數(shù)組乳愉,函數(shù),甚至另一個(gè)代理)
handler 一個(gè)通常以函數(shù)作為屬性的對(duì)象诅福, 各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為匾委。
handler.get() 方法用于攔截對(duì)象的讀取屬性操作。
handler.set() 方法是設(shè)置屬性值操作的捕獲器氓润。
handler.deleteProperty() 方法用于攔截對(duì)對(duì)象屬性的 delete 操作

Reflect 是一個(gè)內(nèi)置的對(duì)象赂乐,它提供攔截 JavaScript 操作的方法這些方法與proxy handlers的方法相同。

Reflect.get(target, propertyKey[, receiver])
Reflect.deleteProperty(target, propertyKey)
Reflect.set(target, propertyKey, value[, receiver])
target: 需要取值的目標(biāo)對(duì)象咖气; key: 需要獲取的值的鍵值挨措;value::設(shè)置的值挖滤。
如果target對(duì)象中指定了getter, receiver則為getter調(diào)用時(shí)的this值

1. Proxy對(duì)數(shù)據(jù)攔截監(jiān)聽的基本使用

1浅役、用Proxy將目標(biāo)對(duì)象data進(jìn)行包裝攔截處理如下:

const proxyData = new Proxy(data, {
    get(target, key, receiver) {
        const result = Reflect.get(target, key, receiver)
        console.log('get', key) //監(jiān)聽
        return result //返回結(jié)果
    },
    set(target, key, value, receiver) {
        const result = Reflect.set(target, key, value, receiver)
        console.log('set', key, value) //set age 30
        return result //是否設(shè)置成功
    },
    deleteProperty(target, key) {
        const result = Reflect.deleteProperty(target, key)
        console.log('delete property', key) //delete property name
        return result //是否刪除成功
    },
})

2斩松、定義data為對(duì)象,并對(duì)data對(duì)象進(jìn)行操作時(shí)

const data = {
    name: 'lili',
    age: 20,
}
// 對(duì)象操作
proxyData.age // get操作  :  get age
proxyData.age = 30 // set操作 :  set age 30
proxyData.sex = "女" // set操作 :  set sex 女
delete proxyData.name // 刪除操作 : delete property name

不足:
在對(duì)象設(shè)置屬性時(shí)觉既,無法確定是新增屬性還是原有屬性惧盹;

3、定義data為數(shù)組瞪讼,并對(duì)data數(shù)組進(jìn)行操作

const data =['a','b','c']

proxyData.push('d') 
// get push  push()方法觸發(fā)
// get length //獲取數(shù)組長度
// set 3 d //設(shè)置值
// set length 4 設(shè)置數(shù)組長度

不足:
給數(shù)組添加元素時(shí)钧椰,沒必監(jiān)聽 原型的屬性,如push()符欠,只需要監(jiān)聽本身(非原型)的屬性嫡霞,
set 3 d,set length 4 為重復(fù)處理同一個(gè)數(shù)據(jù)希柿,set length 4多余

2. Proxy對(duì)數(shù)據(jù)攔截監(jiān)聽使用的優(yōu)化

針對(duì)以上問題诊沪,對(duì)Proxy對(duì)數(shù)據(jù)攔截監(jiān)聽使用進(jìn)行優(yōu)化

1.在對(duì)象設(shè)置屬性時(shí),無法確定是新增屬性還是原有屬性
在set方法中判斷

const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
    console.log('已有的 key') //監(jiān)聽
} else {
    console.log('新增的 key')
}

2曾撤、在監(jiān)聽屬性時(shí)端姚,只監(jiān)聽本身(非原型)的屬性

在get方法中,判斷如果是自身的屬性盾戴,才進(jìn)行監(jiān)聽

// Reflect.ownKeys()方法可以返回包含Symbol屬性在內(nèi)的自有屬性寄锐。
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
    console.log('get',key)//監(jiān)聽
}

3、重復(fù)的數(shù)據(jù)不處理

在 set方法中尖啡,重復(fù)數(shù)據(jù)不處理

const oldVal=target[key]
if (value === oldVal) {
    return true
}

以上問題,Proxy對(duì)數(shù)據(jù)攔截監(jiān)聽使用的優(yōu)化后:

const proxyData = new Proxy(data, {
  // target:目標(biāo)對(duì)象剩膘、 key:被捕獲的屬性名衅斩、receiver:Proxy或者繼承Proxy的對(duì)象
  get(target, key, receiver) {
    // 只監(jiān)聽 處理本身(非原型)的屬性
    const ownKeys = Reflect.ownKeys(target)
    if (ownKeys.includes(key)) {
      console.log('get', key) //監(jiān)聽
    }
    const result = Reflect.get(target, key, receiver)
    return result //返回結(jié)果
  },
  // value 新屬性值。
  set(target, key, value, receiver) {
        const ownKeys = Reflect.ownKeys(target)
    if (ownKeys.includes(key)) {
      console.log('已有的 key') //監(jiān)聽
    } else {
      console.log('新增的 key')
    }
    // 重復(fù)的數(shù)據(jù)不處理
    if (value === target[key]) {
      return true
    }
    const result = Reflect.set(target, key, value, receiver)
    console.log('set', key, value) //set age 30
    return result //是否設(shè)置成功
  },
  deleteProperty(target, key) {
    const result = Reflect.deleteProperty(target, key)
    console.log('delete property', key) //delete property name
    return result //是否刪除成功
  },
})

定義data為數(shù)組怠褐,并對(duì)data數(shù)組進(jìn)行操作時(shí)

const data =['a','b','c']

proxyData.push('d') 
// get length //獲取數(shù)組長度
// set 3 d //設(shè)置值

實(shí)現(xiàn)了只保留了對(duì)自身屬性的監(jiān)聽畏梆,重復(fù)數(shù)據(jù)沒有重復(fù)設(shè)置

3. Proxy實(shí)現(xiàn)響應(yīng)式

實(shí)現(xiàn)思路:
① 創(chuàng)建響應(yīng)式方法reactive(data),該方法可以傳入需要處理的數(shù)據(jù)對(duì)象data
函數(shù)內(nèi)邏輯:
② 判斷 data 是否為 對(duì)象或者數(shù)組奈懒,不是直接返回
③ 創(chuàng)建 Proxy 代理對(duì)象奠涌,Proxy對(duì)象中傳入data
Proxy 代理對(duì)象中的方法配置:
④ 在get()方法對(duì)數(shù)據(jù)的進(jìn)行監(jiān)聽:只監(jiān)聽 處理本身(非原型)的屬性;在返回結(jié)果中采用遞歸調(diào)用reactive(),實(shí)現(xiàn)對(duì)數(shù)據(jù)的深度監(jiān)聽
⑤ 在 set() 方法中進(jìn)行數(shù)據(jù)的新增和更新:判斷是否是新增數(shù)據(jù)磷杏;重復(fù)的數(shù)據(jù)不處理溜畅;
⑥ 在 deleteProperty() 方法中對(duì)數(shù)據(jù)進(jìn)行刪除操作;
實(shí)例:
⑦ 定義數(shù)據(jù)data,傳入響應(yīng)式方法中极祸,返回的值proxyData為實(shí)現(xiàn)響應(yīng)式的可操作數(shù)據(jù)

// 創(chuàng)建響應(yīng)式
function reactive(target = {}) {
  if (typeof target != 'object' || target == null) {
    // 不是對(duì)象或者數(shù)組慈格,則返回
    return target
  }
  // 代理配置 生成代理對(duì)象
  return observed = new Proxy(target, {
    // target:目標(biāo)對(duì)象怠晴、 key:被捕獲的屬性名、receiver:Proxy或者繼承Proxy的對(duì)象
    get(target, key, receiver) {
      // 只監(jiān)聽 處理本身(非原型)的屬性 ,如push()
      const ownKeys = Reflect.ownKeys(target)
      if (ownKeys.includes(key)) {
        console.log('get', key) //監(jiān)聽
      }
      const result = Reflect.get(target, key, receiver)
      // return result //返回結(jié)果
      // 深度監(jiān)聽
      // 性能如何提升的浴捆?
      return reactive(result) //遞歸get處理 實(shí)現(xiàn)深度監(jiān)聽
    },
    // value 新屬性值蒜田。
    set(target, key, value, receiver) {
      // 判斷是否是新增屬性
      const ownKeys = Reflect.ownKeys(target)
      if (ownKeys.includes(key)) {
        console.log('已有的 key') //監(jiān)聽
      } else {
        console.log('新增的 key')
      }
      // 重復(fù)的數(shù)據(jù)不處理
      const oldVal = target[key]
      if (value === oldVal) {
        return true
      }
      const result = Reflect.set(target, key, value, receiver)
      console.log('set', key, value) //set age 30
      return result //是否設(shè)置成功
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key)
      console.log('delete property', key) //delete property name
      console.log('result', result) //result true
      return result //是否刪除成功
    },
  })

}


// 測試數(shù)據(jù)
const data = {
  name: 'lili',
  age: 20,
  info: {
    city: "beijing"
  }
}
const proxyData = reactive(data)

vue3.0 與 vue2.* 實(shí)現(xiàn)響應(yīng)式 比較

vue3.0 基于 Proxy 實(shí)現(xiàn)響應(yīng)式
vue2.0 基于 Oject.defineProperty 實(shí)現(xiàn)響應(yīng)式

相較于vue2,Proxy 實(shí)現(xiàn)響應(yīng)式:

  • 深度監(jiān)聽,性能更好
  • 可監(jiān)聽新增刪除的屬性
  • 可監(jiān)聽數(shù)組變化

總結(jié):

  • Proxy 能規(guī)避 Oject.defineProperty的問題
  • Proxy無法兼容所有瀏覽器选泻,無法 polyfill

Reflect的作用總結(jié)

  • 1.和 Proxy 能力一一對(duì)應(yīng)

  • 2.規(guī)范化冲粤、標(biāo)準(zhǔn)化、函數(shù)式

const obj={a:100,b:200}
'a' in obj //true
Reflect.has(obj,'a')//true

delete obj.b //true
Reflect.deleteProperty(obj,'b') //true
  • 3.替代掉Object上的工具函數(shù)
const obj={a:100,b:200}
Object.getOwnPropertyNames(obj) //["a", "b"]
Reflect.ownKeys(obj) // ["a", "b"]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末页眯,一起剝皮案震驚了整個(gè)濱河市色解,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌餐茵,老刑警劉巖科阎,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異忿族,居然都是意外死亡锣笨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門道批,熙熙樓的掌柜王于貴愁眉苦臉地迎上來错英,“玉大人,你說我怎么就攤上這事隆豹⊥盅遥” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵璃赡,是天一觀的道長判哥。 經(jīng)常有香客問我,道長碉考,這世上最難降的妖魔是什么塌计? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮侯谁,結(jié)果婚禮上锌仅,老公的妹妹穿的比我還像新娘。我一直安慰自己墙贱,他們只是感情好热芹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惨撇,像睡著了一般伊脓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上串纺,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天丽旅,我揣著相機(jī)與錄音椰棘,去河邊找鬼。 笑死榄笙,一個(gè)胖子當(dāng)著我的面吹牛邪狞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茅撞,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼帆卓,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了米丘?” 一聲冷哼從身側(cè)響起剑令,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拄查,沒想到半個(gè)月后吁津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堕扶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年碍脏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稍算。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡典尾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糊探,到底是詐尸還是另有隱情钾埂,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布科平,位于F島的核電站褥紫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏匠抗。R本人自食惡果不足惜故源,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汞贸。 院中可真熱鬧,春花似錦印机、人聲如沸矢腻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽多柑。三九已至,卻和暖如春楣责,著一層夾襖步出監(jiān)牢的瞬間竣灌,已是汗流浹背聂沙。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留初嘹,地道東北人及汉。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像屯烦,于是被迫代替她去往敵國和親坷随。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容