Vue2和Vue3不同

INFO

Vue2到Vue3的性能提升,原因不能简单概括为defineProperty到Proxy的转变。从本质上看,Proxy基于反向代理的性能反而比defineProperty差。

根本原因是diff算法的优化、patch等多原因,而不是Proxy特性本身。

响应式

Object.defineProperty

//Vue2 Object.defineProperty
const initData = {
  value:1
}
const data = {}
Object.keys(initData).forEach(key=>{
  Object.defineProperty(data,key,{
    get(){
      console.log('访问',key)
      return initData[key]
    },
    set(val){
      console.log('设置值',key)
      initData[key] = val
    }
  })
})
// 控制台调试 设置data.value=3,发现data没变,initData变化了

this.$set(data,a,1)

$set的目的是给指定对象添加响应式属性后,并触发视图的更新

function set(target:Array<any> | Object,key:any,val:any):any{
  //isUndef是判断target是不是等于undefined或者null
  //isPrimitive是判断target的数据类型是不是string、number、symbol、boolean中的一种
  if(process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target)){
    warn(`Cannot set reactive property on undefined,null,or primitive value:${{target:any}}`)
  }

  //数组的处理
  if(Array.isArray(target) && isValidArrayIndex(key)){
    target.length = Math.max(target.lenght,key)
    target.splice(key,1,val) //splice(index,length,val)修改下标为index,长度为length的值为val
    return val //当使用splice方法后,原target的被更新,触发Vue2的响应式
  }

  //对象,并且该属性原来已存在于对象中,则直接更新
  if(key in target && !(key in Object.prototype)){
    target[key] = val
    return val
  }

  //vue 给响应式对象(比如data里定义的对象)都加了 _ob_ 属性
  //若一个对象里有 _ob_ 属性,那么就说明这个对象是响应式对象,修改对象已有属性时就会触发页面渲染
  //非 data 里定义的就不是响应式对象(以下是判断是否为响应式对象)
  const ob = (target:any)._ob_

  if(target._isVue || (ob && ob.vmCount)){
    process.env.NODE_ENV !== 'product' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $date' +
      'at runtime - declare it upfront in the data options.'
    )
    return val
  }

  //不是响应式对象
  if(!ob){
    target[key] = val
    return val
  }

  //是响应式对象,进行依赖收集
  defineReactive(ob.value,key,val)

  //触发更新视图
  ob.dep.notify()
  return val
}

Proxy

结合Object.defineProperty,可以去说明vue2和vue3在响应式这块如何获取和修改

const initData = { value:1 }
const proxy = new Proxy(initData,{
  get:function(target,key,receiver){
    console.log('访问',key)
    return Reflect.get(target,key,receiver)
  },
  set:function(target,key,value,receiver){
    console.log('修改',key)
    return Reflect.set(target,key,value,receiver)
  }
})
// 控制台调试 设置proxy.value2 = 2,proxy和initData都变化了

proxy也有很多 ES6 的写法,通过 polifill 转成 ES5

衍生:如何将 esNext => es5? 常用的是 babel 去转,国外主流使用 swc(Rust编写)