vue2与vue3的响应式原理

376 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前言

我们知道,vue3相对于vue2更快、更轻、更贴近原生、更易于维护,为了实现这些效果,vue3在很多方面进行了重构,其中包括新的响应式机制。本篇文章主要讲讲vue2和vue3响应式的区别。

vue2与vue3的响应式原理

vue2

  • vue2响应式原理
    • 对象类型:通过Object.definedProperty()进行数据劫持,
      • set:新增数据调用
      • get:获取数据调用
    • 数组类型:通过重写更新数组的方法实现拦截
  • 缺点:
    • 新增属性、删除属性,界面不会更新
    • 直接通过下标修改数组,界面不会自动更新

代码

<template>
  <div>
    <h1>姓名:{{person.name}}</h1>
    <h1 v-show="person.sex">性别:{{person.sex}}</h1>
    <h1 v-show="person.hobby">爱好:{{person.hobby}}</h1>
    <button @click="updataSex">新增性别</button>
    <button @click="deleteSex">删除性别</button>
    <button @click="updataHobby">修改数组</button>
  </div>
</template>

<script>

export default {
  data () {
    return {
      person: {
        name: '张三',
        hobby: ['吃饭', '睡觉']
      }
    }
  },
  methods: {
    updataSex () {
      // this.person.sex = '男' // 实际上改了,只是没有更新到DOM
      // 两种方法实现 响应式 新增数据
      // vue.set(this.person, 'sex', '男')
      this.$set(this.person, 'sex', '男')

    },
    deleteSex () {
      // delete this.person.sex // 实际上改了,只是没有更新到DOM
      this.$delete(this.person, 'sex')
      // vue.delete(this.person, 'sex')
    },
    updataHobby () {
      // this.person.hobby[0] = '学习'// 实际上改了,只是没有更新到DOM
      // 两种方法实现 响应式 更新数据
      // this.$set(this.person.hobby[0], 0, '学习')
      this.person.hobby.splice(0, 1) // 重写数组方法
    }
  },
}
</script>


GIF4

注意点:在vue3的项目中,不能使用this.$set,因为vue3导入的是vue的工厂函数createApp,里面没有包含this.$set,需要在vue2中执行

vue3

  • 实现原理
    • 通过Proxy:拦截对象属性的变化(属性值的:增删改查)
    • 通过Reflect:对被代理对象的属性进行操作
  • 解决的问题
    • 解决了vue2中存在的问题(新增数据界面不更新)

实现原理

  • 简单实现vue2的响应式
const p1 = {}
Object.defineProperty(p1,'name',{
	get(){
		return person.name
	},
	set(value){
		person.name = value
	}
})
  • 简单实现vue3的响应式
const p1 = new Proxy(person,{
  // 数据被获取时,调用
  get(target,propName){
    return target[propName]
  },
  // 数据被修改或新增时,调用
  set(target,propName,value){
    target[propName] = value
  },
  // 数据被删除时,调用
  deleteProperty(target,propName){
    return delete target[propName]
  }
})

通过 target[propName],可以拦截任意属性的变化,不需要像vue2中需要提前知道哪个属性被操作,到这里,数据的响应式已经实现了

但是vue底层并不仅仅是,找到target[propName],然后操作数据,而是通过Reflect来操作数据

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法

它存在的意义就是,如果操作对象出现报错,会返回false

改进vue3的响应式

const p1 = new Proxy(person,{
  // 数据被获取时,调用
  get(target,propName){
    return Reflect.get(target,propName)
  },
  // 数据被修改或新增时,调用
  set(target,propName,value){
    Reflect.set(target,propName,value)
  },
  // 数据被删除时,调用
  deleteProperty(target,propName){
    return Reflect.deleteProperty(target,propName)
  }
})

这才是vue3中实现响应式的原理

总结

我们在使用vue2新增属性和删除属性的时候,都需要通过Object.definedProperty()劫持数据才能实现响应式,在vue3中,通过Proxy的形式,解决了这个痛点。