这里写自定义目录标题
- 1、两者的区别
- 2、用法
- 3、vue3中声明的数组/对象
- 3.1 通过reactive 声明的Array/Object,需要重新分配一个全新的对象,会出错、或失去响应式效果
-
3.2 解决方案
- 4、cosnt 说明
- 5、Proxy 与 defineProperty
1、两者的区别
ref
:通常用于定义一个响应式引用,例如Number、String、Boolean、Object、Array等- 可以用于定义一个基本数据类型、或者引用数据类型的响应式引用,返回的是一个带有
.value
属性的对象,但它的.value
是一个 Proxy对象,这使得 Vue 也能够正确追踪和响应这些引用的变化 - 更简单直观,通过
.value
来访问和修改值,代码量少,易于理解 - 如果是基本类型(单一数据值),
ref
也会将它转换成响应式数据,便于监听数据变化
- 可以用于定义一个基本数据类型、或者引用数据类型的响应式引用,返回的是一个带有
reactive
:用于定义一个的响应式对象,例如Array、Object等;- 仅用于定义一个引用类型的响应式对象,返回的是深度响应的Proxy对象,对象的任何数据发生变化时(增删改)都会被监测到
- 必须是不需要重新分配一个全新的对象的对象
若重新赋值,会报错或造成响应式丢失,建议使用ref
,详细解释看下方的 vue3中声明数组
1、无论是ref、reactive最终返回的值都可以监听追踪到值的变化
2、reactive仅用于定义引用数据类型,这是因为底层逻辑用的是
Proxy
实现的,Proxy
不适用 基本数据类型
尽量不要用reactive
定义基本类型,有警告错误
3、简单解释一下“重新分配一个全新的对象”,用ref
更合适的原因:
ref
返回的是响应式引用,在修改值时,用的是.value
,而不是直接修改整个ref
而reactive
返回的是响应式对象,在修改值时,是直接赋值(state={}),等同于改掉了整个对象
响应式引用与响应式对象:
响应式引用
:提供访问 和 修改原始值的方法,并不会深层追踪对象内部属性的变化。在处理基本数据类型时,会简单的包装原始值,而处理对象时,会对对象进行浅层包装,使得可以追踪对象引用的变化,但不会深层追踪对象内部属性的变化。
所以通过 ref
声明的 层级太多的对象,可能会监测不到所有内部属性的变化,【目前笔者还未测试出来】
响应式对象
:会追踪其内部属性的变化,当属性值被修改时,视图也会自动更新
2、用法
- 在
setup()
中使用<template> <div>{{ count }} </div> <div>{{ state.age}} </div> <button @click="changeCount">修改count</button> <button @click="changeAge">修改Age</button> </template> <script> import { ref, reactive } from 'vue'; export default { setup() { const count = ref(0); const state = reactive({ name: 'Alice', age: 30 }); function changeCount() { count.value++; } function changeAge() { state.age++; } return { count, state, changeCount, // 将方法暴露出去 changeAge // 将方法暴露出去 }; } }; </script>
- 在
<script setup>
中使用<template> <div>{{ count }} </div> <div>{{ state.age}} </div> <button @click="changeCount">修改count</button> <button @click="changeAge">修改Age</button> </template> <script setup> import { ref, reactive } from 'vue'; const count = ref(0); const state = reactive({ name: 'Alice', age: 30 }) function changeCount() { count.value++; } function changeAge() { state.age++; } </script>
3、vue3中声明的数组/对象
ref
,这是为了避免 对 reactive
定义的值进行重新分配一个全新的对象时,导致的响应式丢失问题。
当然,如果不是重新分配一个全新的对象,推荐用 reactive
,具体讲解请看 3.2 解决方案
案例如下:
<template>
<div>
refList
</div>
<div v-for="(v, i) in refList" :key="i">
{{ v }}
</div>
<button @click="changeRef">修改ref</button>
<div>
reactive
</div>
<div v-for="(v, i) in reactiveList" :key="i">
{{ v }}
</div>
<button @click="changeReactive">修改reactive</button>
</template>
<script setup>
import { ref, reactive } from 'vue'
const refList = ref([])
const reactiveList = reactive([])
function changeRef() {
// 改变 refList
}
function changeReactive() {
// 改变 reactiveList
}
</script>
3.1 通过reactive 声明的Array/Object,需要重新分配一个全新的对象,会出错、或失去响应式效果
- 用
const
声明的Array
,重新赋值时会报错【提示它是一个只读常量】,这等同于给它重新分配一个全新的对象,const
声明的Object
也一样
const reactiveList = reactive([1, 2, 3])
function changeReactive() {
reactiveList = ['01', 1, 2] // 'reactiveList' is constant. eslint[...]
}
//const reactiveList = reactive({ '0': 1,'1': 2,'2': 3 })
// function changeReactive() {
// reactiveList = { '0': '01','1': 2,'2': 3 } // 'reactiveList' is constant. eslint[...]
// }
- 用
let
声明的Array
,重新赋值时可以赋值成功,但它失去了响应式效果,用let
声明的Object
也一样
let reactiveList = reactive([1, 2, 3])
function changeReactive() {
reactiveList = ['01', 2, 3]
console.log(reactiveList) // 输出结果是['01', 2, 3],但页面渲染还是[1, 2, 3]
}
// let reactiveList = reactive({ '0': 1,'1': 2,'2': 3 })
// function changeReactive() {
// reactiveList = { '0': '01','1': 2,'2': 3 }
// console.log(reactiveList) // 输出结果是['01', 2, 3],但页面渲染还是[1, 2, 3]
// }
3.2 解决方案
reactive
创建一个深度响应式对象,对 对象的所有嵌套属性进行响应式处理,说简单点,就是。ref
则是创建一个包含原始值的响应式引用(ref)。当 ref 的值改变时,会触发依赖更新。
方法一:用 ref
声明 Array,重新分配一个新对象时,不会失去响应,Object[const]也一样
const refList = ref([1, 2, 3])
function changeReactive() {
refList.value = ['01', 2, 3]
console.log(refList.value) // 输出结果是['01', 2, 3],页面渲染也是['01', 2, 3]
}
const refList = ref({ '0': 1,'1': 2,'2': 3 })
function changeRef() {
refList.value = { '0': '01','1': 2,'2': 3 }
console.log(refList.value)
}
方法二:用 reactive
声明的Array,修改时使用Array.push、splice等方法,object同理
const reactiveList = reactive([1, 2, 3])
function changeReactive() {
reactiveList.push(4) // 输出结果是['01', 2, 3, 4],页面渲染也是[1, 2, 3, 4]
console.log(reactiveList)
}
4、cosnt 说明
在 Vue 2 中,使用 const 声明的变量确实是 常量
,因为 Vue 2 的响应式系统是基于 Object.defineProperty 实现的,无法追踪 const 变量的重新赋值。
但在 Vue 3 中,采用了基于 Proxy 的新响应式系统,const 声明的变量依然可以是响应式的。
在vue3的 setup
函数中,const
声明的变量被称之为 响应式引用 或 响应式对象
5、Proxy 与 defineProperty
reactive方法内部是利用ES6的Proxy API来实现的,这里与Vue2中的defineProperty方法有本质的区别。
- defineProperty只能单一地监听已有属性的修改或者变化,无法检测到对象属性的新增或删除,而Proxy可以轻松实现;
- defineProperty无法监听属性值是数组类型的变化,而Proxy可以轻松实现。
备注:
如有理解错误的观点,请在评论区留言,接受批评和指导