Vue3响应式

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Vue3响应式

​ 奔跑吧鸡翅   ​   2022-06-19 我要评论

Reflect

回顾 vue2.x 的响应式

实现原理:

  • 对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
  • 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹)
Object.defineProperty(data,'count ",{
	get(){},
	set(){}
})

存在问题:

  • 新增属性、删除属性,界面不会更新
  • 直接通过下标修改数组,界面不会自动更新

但是 vue2 给了解决方法,我们看以下代码:

<template>
  <div>
    <h2>我是vue2写的效果</h2>
    <h4 v-show="person.name">姓名:{{person.name}}</h4>
    <h4>年龄:{{person.age}}</h4>
    <h4 v-show="person.sex">性别:{{person.sex}}</h4>
    <h4>爱好:{{person.hobby}}</h4>
    <button @click="addSex">添加sex属性</button>
    <button @click="deleteName">删除name属性</button>
    <button @click="changeHobby">修改爱好</button>
  </div>
</template>

<script>
import Vue from 'vue'
export default {
  name: 'App',
  data(){
    return{
      person:{
        name:'张三',
        age:18,
        hobby:['学习','吃饭']
      }
    }
  },
  methods:{
    addSex(){
      //这样直接加是不行的
      //this.person.sex = '男'
      this.$set(this.person,"sex",'男')
      //Vue.set(this.person,"sex",'男')
    },
    deleteName(){
      //这样直接加是不行的
      //delete this.person.name
      //this.$delete(this.person,'name')
      Vue.delete(this.person,"name")
    },
    changeHobby(){
      //这样直接加是不行的
      //this.person.hobby[0] = '逛街'
      //可以这样
      this.$set(this.person.hobby,0,'逛街')
      //或
      //this.person.hobby.splice(0,1,"逛街")
    },
  }
}
</script>

我们可以用 js 模拟 vue2 的响应式:

<script>
        //源数据
        let person = {
            name:"张三",
            age:18
        }
        let p = {}
        //模拟vue2实现响应式
        Object.defineProperty(p,"name",{
        	configurable:true,
            get() {//有人读取name时调用
                return person.name
            },
            set(v) {
                person.name = v
                console.log("有人修改了name属性,我发现了,我要去更新界面");
            }
        })
        Object.defineProperty(p,"age",{
            get() {//有人读取age时调用
                return person.age
            },
            set(v) {
                person.age = v
                console.log("有人修改了age属性,我发现了,我要去更新界面");
            }
        })
</script>

先输出 person,然后看下 p,当修改 name 或 age 时会检测到

它的问题是,如果增加一个 sex 属性,vue 不会检测到,虽然增加了 sex 属性,但它不像 name 和 age 有 getter 和 setter,不是响应式的

同样,当删除 name 属性时也监测不到

vue3的响应式

<template>
  <h1>一个人的信息</h1>
  <h3 v-show="person.name">姓名:{{ person.name }}</h3>
  <h3>年龄:{{ person.age }}</h3>
  <h3 v-show="person.sex">性别:{{ person.sex }}</h3>
	......
  <button @click="changeInfo">修改人的信息</button>
  <button @click="addSex">添加一个sex属性</button>
  <button @click="deleteName">删除一个name属性</button>
</template>

<script>
import {reactive} from 'vue'

export default {
  name: 'App',
  setup() {
	......

    function changeInfo() {
      ......
      person.hobby[0] = '学习'
    }
    
    function addSex() {
      person.sex = "男"
    }

    function deleteName() {
      delete person.name
    }

    return {
      ......
      addSex,
      deleteName
    }
  }
}
</script>

模拟 vue3 中的响应式:

    <script>
        //源数据
        let person = {
            name:"张三",
            age:18
        }
        const p = new Proxy(person,{
            //有人读取p的某个属性时调用
            get(target, p, receiver) {
                console.log(`有人读取了p身上的${p}属性`);
                //return target[p]
                return Reflect.get(target,p)
            },
            //有人修改、增加p的某个属性时调用
            set(target, p, value, receiver) {
                console.log(`有人修改了p身上的${p},我要去更新界面了`);
                //target[p] = value
                Reflect.set(target,p,value)
            },
            //有人删除p的某个属性时调用
            deleteProperty(target, p) {
                console.log(`有人删除了p身上的${p},我要去更新界面了`);
                //return delete target[p]
                return Reflect.deleteProperty(target,p)
            }
        })
    </script>

实现原理:

  • 通过Proxy(代理)∶拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等
  • 通过Reflect(反射):对源对象的属性进行操作

MDN文档中描述的 Proxy 与 Reflect

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。Reflect不是一个函数对象,因此它是不可构造的。

new Proxy(data,{
	//拦截读取属性值
	get (target, prop){
		return Reflect.get(target,prop)
	},
	//拦截设置属性值或添加新属性
	set (target,prop, value) {
		return Reflect.set(target,prop, value)
	},
	//拦截删除属性
	deleteProperty (target,prop) {
		return Reflect.deleteProperty(target,prop)
	}
})
proxy.name = "tom"

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们