揭幕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++复制代码
相关文章
相关标签/搜索