Open jtwang7 opened 2 years ago
参考文章:一文搞懂Vue3的响应式函数ref和reactive
✅ 作用:声明响应式数据 Vue3中通过ref() 和reactive()函数来声明响应式数据。 ref()可以声明任意类型的响应式数据。reactive()只能声明对象(数组)类型的响应式数据。
ref()
reactive()
let tom: Person = { age: 30, name: "cat" } //1。通过ref声明响应式数据 const ref1 = ref<number>(1);//基础类型数据 const ref2 = ref<Person>(tom); //对象/数组类型数据 //2. 通过reactive声明相适应数据 const number = reactive(1);//报错不能声明基本类型的数据,只能传入一个对象。 const reactive1 = reactive(tom);//可以
✅ reactive() 函数 reactive()函数内部通过 New Proxy()的方式来对数据进行代理。从而实现响应式。由于Proxy的限制,只能用来创建对象类型的响应式数据。 通过reactive()函数,会得到一个代理对象(也叫响应式对象),只有通过代理对象操作属性,才是响应式的,通过原对象操作属性是不会响应。
New Proxy()
let tom: Person = { age: 30, name: "cat" } const reactive1 = reactive(tom); //返回代理对象。(也叫响应式对象) reactive1.name = "cat";//操作代理对象,响应式。 tom.name = "mi"; //操作原对象,不是响应式。
这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理对象。
let tom: Person = { age: 30, name: "cat" } const reactive1 = reactive(tom); const reactive2 = reactive(tom); const reactive3 = reactive(reactive2); console.log(reactive1 == reactive2); //true console.log(reactive3 == reactive2); //true console.log(reactive3 == reactive1); //true
Proxy是浅代理,对于嵌套的对象的不代理的,reactive() 之所以深层次代理是因为它内部对嵌套对象做了递归代理处理。
Proxy
let tom: Person = { age: 30, name: "cat", sex: { ho: "02", sex: "男" }, } const reactive1 = reactive(tom); console.log(reactive1.sex); //通过打印即可验证,打印出来的是一个响应式对象
只能处理对象类型:
不能更改响应式对象引用:
响应式对象的相同引用
let tom: Person = { age: 30, name: "cat" } let reactive1 = reactive(tom); reactive1 = { age: 0, name: "cat" }//将响应式对象的变量赋值给另一个对象,丢失响应式 //修改嵌套响应式对象里面嵌套的对象引用可以,因为是深层代理。 let tom: Person = { age: 30, name: "tom", sex: { ho: "02", sex: "男" }, } let reactive1 = reactive(tom); reactive1.sex = { ho: "03", sex: "女" }
属性赋值失去响应式:
当我们将响应式对象的 属性 赋值给另一个变量、使用解构赋值、将属性传入一个函数中,我们会失去响应性。
属性
只对基本数据类型有限制。如果属性的类型是对象类型,则不受限制。
原因:
基本数据类型是值传递,而对象类型是引用传递。
let tom: Person = { age: 30, name: "cat" } let reactive1 = reactive(tom); let ng: string = reactive1.name; // 将属性赋值给另一个变量,ng变量不是响应式。 let {age, name} = reactive1; // 使用解构赋值,得到的属性不是响应式。 setName(reactive1.name);//将属性传入函数中,该属性不是响应式 function setName(name: string) { name = "cat02" } //属性是一个对象类型。 let tom: Person = { age: 30, name: "cat", sex: { ho: "02", sex: "男" }, } let reactive1 = reactive(tom); setName(reactive1.sex) function setName(sex: Sex) { sex.sex = "女";//可以修改,是响应式。 } let {sex: sex1} = reactive1; sex1.ho = "sssss";//可以修改,是响应式。
✅ ref() 函数 为了解决 reactive() 带来的限制,Vue 也提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref对象。 通过ref()函数创建响应式的数据,会返回一个RefImpl对象(也叫ref对象),响应式数据会被封装到ref对象的.value属性中。(.value属性就是响应式属性)
RefImpl
ref对象
.value
如果是基本类型的数据,那么ref函数就会使用Object.defineProperty()来实现对数据的劫持。
ref
Object.defineProperty()
如果是对象(数组)类型的数据,则ref函数就会调用reactive()函数。而.value就是代理对象
let tom: Person = { age: 30, name: "tom", } const ref1 = ref(1);//基本类型 console.log(ref1.value); const ref2 = ref<Person>(tom);//对象类型 console.log(ref2.value); //可以看到时一个代理对象
使用ref()函数创建响应式的数据,是可以更改对象的引用的。(通过reactive函数的是不能替换)
const person = ref<Person>({ age: 20, name: "tom" }); let tom: Person = { age: 30, name: "cat" } person.value = tom;//可以替换,因为当值发表改变了就会调用reactive函数。
一言以蔽之,ref() 使我们能创造一种任意值的 “引用” 并能够不丢失响应性地随意传递。这个功能非常重要,因为它经常用于将逻辑提取到 组合函数 中。(Vue官网)
🔆 reactive 和 ref的区别
reactive
proxy
✅ 个人理解
为什么 ref() 创建的响应式数据,可以更改对象的引用,而 reactive() 不可以? 答: 从本质上讲两者创建的响应式数据都是不可以被更改引用的。响应式对象被创建的时候就会被注入到响应系统内部,如果强行覆盖的话,相当于抛弃了响应式对象而去转而使用一个非响应式的对象。
什么情况下赋值不会丢失响应? 答: 首先我们要清楚为什么会丢失响应,无非是你当前操作的变量它没有在响应系统中注册,那么什么变量算是在响应系统中注册的呢?通过 Vue 提供的 API 接口 (例如 ref / reactive 等) 生成的变量。当我们将这些变量赋值给新创建的变量时,由于新创建的变量本身是没有在响应系统中注册过的,所以它不具备响应性,但是!赋值又分为两种情况,值传递和引用传递:
因此,当我们复制一个对象引用时 (此处包括 ref 对象类型的嵌套对象,因为 vue 对它进行了深层代理),会保留其响应性;当我们复制一个基本类型时 (例如从 ref 对象属性中抽出一个基本类型值赋给新的变量),就会丢失响应。
Vue3 初探 ref 与 reactive 响应式原理
参考文章:一文搞懂Vue3的响应式函数ref和reactive
✅ 作用:声明响应式数据 Vue3中通过
ref()
和reactive()
函数来声明响应式数据。ref()
可以声明任意类型的响应式数据。reactive()
只能声明对象(数组)类型的响应式数据。✅
reactive()
函数reactive()
函数内部通过New Proxy()
的方式来对数据进行代理。从而实现响应式。由于Proxy的限制,只能用来创建对象类型的响应式数据。 通过reactive()
函数,会得到一个代理对象(也叫响应式对象),只有通过代理对象操作属性,才是响应式的,通过原对象操作属性是不会响应。reactive()
函数的设计是单例模式:对同一个对象多次调用reactive()
函数,返回的都是同一个代理对象。对一个代理对象调用reactive()
函数,总会返回代理对象自身。reactive()
是深层次的代理,响应式对象内的嵌套对象依然是代理对象。并且也是单例模式。reactive()
函数的局限性只能处理对象类型:
Proxy
的限制,reactive()
函数只对对象类型的数据有效,对于基本数据类型的代理是无效的。不能更改响应式对象引用:
响应式对象的相同引用
。(Vue官网解释)属性赋值失去响应式:
当我们将响应式对象的
属性
赋值给另一个变量、使用解构赋值、将属性传入一个函数中,我们会失去响应性。只对基本数据类型有限制。如果属性的类型是对象类型,则不受限制。
原因:
基本数据类型是值传递,而对象类型是引用传递。
✅
ref()
函数 为了解决reactive()
带来的限制,Vue 也提供了一个ref()
方法来允许我们创建可以使用任何值类型的响应式 ref对象。 通过ref()
函数创建响应式的数据,会返回一个RefImpl
对象(也叫ref对象
),响应式数据会被封装到ref对象的.value
属性中。(.value
属性就是响应式属性)如果是基本类型的数据,那么
ref
函数就会使用Object.defineProperty()
来实现对数据的劫持。如果是对象(数组)类型的数据,则ref函数就会调用
reactive()
函数。而.value
就是代理对象使用
ref()
函数创建响应式的数据,是可以更改对象的引用的。(通过reactive函数的是不能替换)一言以蔽之,
ref()
使我们能创造一种任意值的 “引用” 并能够不丢失响应性地随意传递。这个功能非常重要,因为它经常用于将逻辑提取到 组合函数 中。(Vue官网)🔆 reactive 和 ref的区别
reactive
只能用来创建对象类型的响应式数据,而ref
则是可以创建任意数据类型的响应式对象。reactive
创建的是proxy
的代理对象,而ref创建的则是ref对象。reactive
创建的响应式对象是不能更换对象的引用的,而通过ref
创建响应式对象是可以替换的。reactive
是通过Proxy来对数据进行代理的,而ref
,如果是基本数据类型,通过Object.defineProperty()
来实现对数据的劫持。如果是对象类型的数据,则是通过调用reactive
函数实现。✅ 个人理解
为什么
ref()
创建的响应式数据,可以更改对象的引用,而reactive()
不可以? 答: 从本质上讲两者创建的响应式数据都是不可以被更改引用的。响应式对象被创建的时候就会被注入到响应系统内部,如果强行覆盖的话,相当于抛弃了响应式对象而去转而使用一个非响应式的对象。ref()
创建了一个响应式对象,其主要内容都被挂载在了 value 属性上,因此修改 value 属性上的引用并没有覆盖 ref 创建的响应式对象,所以我们说 ref 是可以修改对象引用的。reactive()
创建响应对象的方式不太一样,对于一个对象而言,它会将对象内所有属性注册 (且深层递归注册) 为响应式,但是对于顶层对象本身而言,它不是响应式的。因此,我们假如覆盖这个对象,相当于把该对象内所有的响应式属性都抛弃了,也就失去了响应性。(将该对象引用赋值给其他变量也是同理,新变量获得的是非响应式对象的引用,因此也不在响应系统的服务范围内)什么情况下赋值不会丢失响应? 答: 首先我们要清楚为什么会丢失响应,无非是你当前操作的变量它没有在响应系统中注册,那么什么变量算是在响应系统中注册的呢?通过 Vue 提供的 API 接口 (例如 ref / reactive 等) 生成的变量。当我们将这些变量赋值给新创建的变量时,由于新创建的变量本身是没有在响应系统中注册过的,所以它不具备响应性,但是!赋值又分为两种情况,值传递和引用传递:
因此,当我们复制一个对象引用时 (此处包括 ref 对象类型的嵌套对象,因为 vue 对它进行了深层代理),会保留其响应性;当我们复制一个基本类型时 (例如从 ref 对象属性中抽出一个基本类型值赋给新的变量),就会丢失响应。