Vue3响应式
奔跑吧鸡翅 人气:0Reflect
回顾 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"
加载全部内容