一、区别
在Vue3中,ref和reactive都是用于创建响应式数据的API,但它们的使用场景有所不同:
-
ref:ref用于将一个简单类型的值(如字符串、数字等)转换成一个响应式对象。ref返回的是一个包含value属性的普通对象,可以直接读取和修改value属性的值。示例代码:
import { ref } from 'vue' const count = ref(0) // 将数字0转换成响应式对象 console.log(count.value) // 0 count.value++ // 直接修改value属性的值 console.log(count.value) // 1 -
reactive:reactive用于将一个普通对象转换成一个响应式对象。reactive返回的是一个响应式对象,可以直接读取和修改对象的属性值。示例代码:
import { reactive } from 'vue' const state = reactive({ count: 0, name: 'vue3' }) console.log(state.count) // 0 state.count++ // 直接修改对象的属性值 console.log(state.count) // 1
因此,ref适用于简单类型的数据,而reactive适用于复杂的数据结构,如对象和数组等。
总的来说,可以按照以下规则来选择使用哪个API:
- 如果你要创建的是简单类型的数据,比如数字、字符串、布尔值等,那么使用
ref。 - 如果你要创建的是复杂的数据结构,比如对象、数组等,那么使用
reactive。
二、.value问题
如果你希望在组件的template中直接使用ref,则需要使用.value来访问值。不过,在组件的setup函数中使用ref时,然后通过return返回,你可以直接访问.value的值了,这样就不会太麻烦了。
如果你觉得这样很麻烦,也可以使用reactive来代替ref,因为在reactive中,可以直接访问和修改对象的属性值,不需要使用.value。不过,需要注意的是,reactive返回的是一个响应式对象,而不是一个普通对象,所以如果你在模板中直接使用reactive,则需要使用$符号来访问属性,例如state.count应该写成state.$count。
总的来说,你可以按照以下规则来选择使用哪个API:
- 如果你需要在组件的
template中直接使用响应式数据,则使用ref,但需要在访问和修改值时使用.value。 - 如果你需要在
setup函数中使用响应式数据,或者你不需要在模板中直接使用响应式数据,则可以使用ref或reactive。如果你要创建的是简单类型的数据,比如数字、字符串、布尔值等,那么使用ref;如果你要创建的是复杂的数据结构,比如对象、数组等,那么使用reactive。
三、Reactive的使用风险
reactive是Vue 3中的响应式API之一,它可以将一个普通的JavaScript对象转换为响应式对象,使得当对象属性发生变化时,相关组件会自动更新视图。虽然reactive很方便,但在使用时也需要注意一些潜在的风险。
其中一个风险是响应式属性的丢失。这种情况通常发生在对响应式对象执行一些非响应式操作时,例如将响应式对象转换为JSON字符串、使用Object.assign合并对象、使用Object.freeze冻结对象,对对象进行展开操作等。这些操作可能会破坏对象的响应式结构,导致某些属性失去响应式。
举个例子,如果我们执行以下操作:
const state = reactive({
count: 0,
todos: []
})
const jsonString = JSON.stringify(state)
const newState = JSON.parse(jsonString)
则newState不再是响应式对象,它的属性将不再具有响应式。同样的,如果我们使用Object.assign合并两个对象,其中一个是响应式对象,另一个是普通对象,则合并后的结果将不再是响应式对象,其中响应式对象的属性将丢失响应式。
虽然reactive很方便,但在使用时也需要注意保持对象的响应式,避免对其执行非响应式操作,以避免响应式属性的丢失。
四、解决
如果你要对一个响应式对象进行JSON.stringify、JSON.parse或者Object.assign操作,但是在操作完成之后想要它仍然保持响应式,你可以使用Vue提供的一些工具函数来帮助你。
toRaw函数:将响应式对象转换为普通对象。可以使用toRaw函数将响应式对象转换为普通对象,然后对普通对象执行JSON.stringify或者Object.assign操作,操作完成后再将普通对象转换回响应式对象即可。例如:
import { reactive, toRaw } from 'vue'
const state = reactive({
count: 0,
todos: []
})
const jsonString = JSON.stringify(toRaw(state))
const newState = Object.assign({}, toRaw(state))
// 对 newState 进行操作之后,将普通对象转换回响应式对象
const reactiveState = reactive(newState)
markRaw函数:将一个对象标记为非响应式对象。如果你确定一个对象不需要响应式,可以使用markRaw函数将其标记为非响应式对象,从而避免对其执行非响应式操作时造成的问题。例如:
import { reactive, markRaw } from 'vue'
const state = reactive({
count: 0,
todos: []
})
const jsonString = JSON.stringify(markRaw(state))
const newState = Object.assign({}, markRaw(state))
// 对 newState 进行操作之后,将普通对象转换回响应式对象
const reactiveState = reactive(newState)
shallowReactive函数:将对象的浅层属性转换为响应式对象。如果你只需要将对象的浅层属性转换为响应式对象,而不需要将嵌套对象转换为响应式对象,可以使用shallowReactive函数。例如:
import { reactive, shallowReactive } from 'vue'
const state = reactive({
count: 0,
todos: []
})
const jsonString = JSON.stringify(shallowReactive(state))
const newState = Object.assign({}, shallowReactive(state))
// 对 newState 进行操作之后,将普通对象转换回响应式对象
const reactiveState = reactive(newState)
总的来说,以上这些工具函数可以帮助你在对响应式对象执行JSON.stringify、JSON.parse或者Object.assign操作时,避免丢失响应式属性,让你的代码更加健壮和可维护。