踩坑了之后,总结了一波vue2和vue3的响应式原理

83 阅读1分钟

1.vue2.x的响应式

实现原理:

  • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
  • 数组类型:通过重写更新数组方法来进行拦截。
Object.defineProperty(data,'shuxin',{
  get(){}
  set(){}
})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 模拟Vue2中的实现响应式 -->
    <script>
      let person = {
          name:'礼盒',
          age:19,
      }
      let p = {}
      Object.defineProperty(p,'name',{
        get(){
        // 有人读取name
           return person.name
        },
        set(value){
        // 有人修改name
           console.log('有人修改了name属性')
           person.name = value
        }
      })
    </script>
</body>
</html>

image.png

删除也不起作用 增加的sex属性也不是响应式的

image.png 遇到的一些问题:

  • 新增属性、删属性、界面不会更新
  • 直接通过下标修改数组时候,界面也不会自动更新
  1. 新增属性的时候界面没有更新耶,但是console出来的对象里面的属性是更新的。也有解决方法:this.setthis.set this.delete/或 Vue.set Vue.delete
  2. 直接改变数组下标的时候界面也不会变化,也可以用this.$set 和Vue.set改变及splice改变界面
<template>
  <div id="app">
    <button @click="addLevel">增加工作职级属性</button>
    <div v-if="job.level">{{job.level}}</div>
    <button @click="deleteType">删除工作类型属性</button>
    <div v-if="job.type">{{job.type}}</div>
    <button @click="updateHobby">改变爱好的第一个元素</button>
    <div>{{hobby}}</div>
  </div>
</template>

<script>
import Vue from 'vue'
export default {
  name: "App",
  data(){
    return {
      job:{
        type:'前端开发',
        salary:'40K',
      },
      hobby:['唱','跳','rapper']
    }
  },
  created(){

  },
  methods: {
    addLevel(){
      console.log(this.job)
      // this.job.level = 'height'
      // this.$set(this.job,'level','height')
      Vue.set(this.job,'level','height')
      console.log(this.job)
    },
    deleteType(){
      console.log(this.job)
      // delete this.job.type
      // this.$delete(this.job,'type')
      Vue.delete(this.job,'type')
      console.log(this.job)
    },
    updateHobby(){
      console.log(this.hobby)
      // this.hobby[1] = '改'
      // this.$set(this.hobby,1,'改')
      // Vue.set(this.hobby,1,'改')
      this.hobby.splice(1,1,'改')
      console.log(this.hobby)
    }
  },
};
</script>

vue3.x

实现原理:

  • 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性的增删、读写
  • 通过Reflect(反射):对被代理对象的属性进行操作。
    <script>
      let person = {
        name: "礼盒",
        age: 19,
      };
      let p = new Proxy(person,{});
    </script>

image.png

    <script>
      let person = {
        name: "礼盒",
        age: 19,
      };
      let p = new Proxy(person,{
        get(target,propName){
            console.log(`读取${propName}属性`)
            // return target[propName]
            return Reflect.get(target,propName)
        },
        set(target,propName,value){
            console.log(`修改了或增加了${propName}属性`)
            // target[propName] = value
            Reflect.set(target,propName,value)
        },
        deleteProperty(target,property){
            console.log(`删除了${propName}属性`)
            // return delete targetName[property]
            return Reflect.deleteProperty(target,propName)
        }
      });
    </script>