一文详解Vue3响应式原理
作者: 奔跑吧鸡翅
这篇文章主要介绍了一文详解Vue3响应式原理,文章通过与vue2.x 的响应式做对比详解展现出了Vue3响应式原理详情,感兴趣的小伙伴可以参考一下
回顾 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"
到此这篇关于一文详解Vue3响应式原理的文章就介绍到这了,更多相关Vue3响应式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!