揭幕Vue3的proxy能作到速度加倍內存減半緣由


image

瞭解過vue3更新內容的同窗應該都知道,vue3的整個數據監聽系統都進行了重構,由es5的Object.defineProperty改成了es6的proxy。尤大說,這個新的數據監聽系統帶來了初始化速度加倍同時內存佔用減半的效果。本文就聊聊,爲啥proxy能夠作到速度加倍的同時,內存還能減半。vue

vue初始化過程


咱們知道,vue的初始化過程,有三座大山,分別爲Observer、Compiler和Watcher,當咱們new Vue的時候,會調用Observer,經過Object.defineProperty來對vue對象的data,computed或者props(若是是組件的話)的全部屬性進行監聽,同時經過compiler解析模板指令,解析到屬性後就new一個Watcher並綁定更新函數到watcher當中,Observer和Compiler就經過屬性來進行關聯,如此,當Observer中的setter檢測到屬性值改變的時候,就調用屬性對應的全部watcher,調用更新函數,從而更新到屬性對應的dom。 各位有興趣的,能夠看下個人另一片文章es6

這裏面有兩點須要強調下:
一、Object.defineProperty須要遍歷全部的屬性,這就形成了若是vue對象的data/computed/props中的數據規模龐大,那麼遍歷起來就會慢不少。
二、一樣,若是vue對象的data/computed/props中的數據規模龐大,那麼Object.defineProperty須要監聽全部的屬性的變化,那麼佔用內存就會很大。數組

Object.defineProperty VS Proxy

Object.definePropety的缺點

除了上面講,在數據量龐大的狀況下Object.defineProperty的兩個缺點外,Object.defineProperty還有如下缺點。
一、沒法監聽es6的Set、WeakSet、Map、WeakMap的變化;
二、沒法監聽Class類型的數據;
三、屬性的新加或者刪除也沒法監聽;
四、數組元素的增長和刪除也沒法監聽。bash

Proxy應運而生

針對Object.defineProperty的缺點,Proxy都可以完美得解決,它惟一的缺點就是,對IE不友好,因此vue3在檢測到若是是使用IE的狀況下(沒錯,IE11都不支持Proxy),會自動降級爲Object.defineProperty的數據監聽系統。因此若是是IE用戶,那麼就享受不到速度加倍,內存減半的體驗了。 dom


初始化對比

Object.defineProperty初始化函數

const data = {}
for(let i = 0; i <= 100000; i++) {
  data['pro' + i] = i
}

function defineReactive(data, property) {
  let value = data[property]
  Object.defineProperty(data, property, {
    get() {
      // console.log(`讀取${property}的值爲${value}`)
      return value
    },
    set(newVal) {
      // console.log(`更新${property}的值爲${newVal}`)
    }
  })
}

for(let property in data) {
  defineReactive(data, property)
}
複製代碼

Proxy初始化ui

const data = {}
for(let i = 0; i <= 100000; i++) {
  data['pro' + i] = i
}
var proxyData = new Proxy(data, {
  get(target, property, receiver) {
    // console.log(`讀取${property}的值爲${target[property]}`)
    return Reflect.get(target, property, receiver);
  },
  set(target, property, value, receiver) {
   //  console.log(`更新${property}的值爲${value}`)
    return Reflect.set(target, property, value, receiver);
  }
})
複製代碼

經過devtool的performace分別對初始化和內存佔用進行對比

分別實例化有1000/10000/100000/1000000個屬性的對象的時間對比 image分別實例化有1000/10000/100000/1000000個屬性的對象的內存對比 imagedp=defineObjectPropertyes5

經過對比咱們知道,Proxy確實能夠作到初始化速度加倍,內存佔用減半的效果,爲啥能作到這兩點,請看開頭我強調的兩點,因此期待vue3的到來吧。spa

暴露響應式API

vue3中暴露了響應式API給用戶進行數據的監控,eg:prototype

import {observable, effect} from 'vue'

const state = observable({
    count: 0
})

effect(() => {
    console.log(`count is: ${state.count}`)
})// count is: 0

state.count++ // count is: 1
複製代碼

根據這部分代碼,我本身也以proxy實現了一個簡單版的,有興趣的瞭解下:

// cbs是Vue的一個靜態屬性,保存了effect的全部回調
let Vue = function() {}
Vue.prototype._cbs = []

/**
* 返回一個代理的對象,參數是須要被代理的對象
**/
const observable = (obj) => {
  return new Proxy(obj, {
    get(target, property, receiver) {
      return Reflect.get(target, property, receiver)
    },
    set(target, property, value, receiver) {
      // 取出全部effect的回調並執行
      for(let cb of Vue.prototype._cbs) {
        cb(value)
      }
      return Reflect.set(target, property, value, receiver)
    }
  })
}

/**
* 只要observable的代理對象屬性改變,就會執行effect的回調
**/
const effect = (cb) => {
  Vue.prototype._cbs.push(cb)
}

let state = observable({
  count: 0
})

effect((value) => {
  console.log(`count is ${value}`)
})
state.count++複製代碼
相關文章
相關標籤/搜索