Ref和Reactive

271 阅读5分钟

一、区别

在Vue3中,refreactive都是用于创建响应式数据的API,但它们的使用场景有所不同:

  1. refref用于将一个简单类型的值(如字符串、数字等)转换成一个响应式对象。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
    
  2. reactivereactive用于将一个普通对象转换成一个响应式对象。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函数中使用响应式数据,或者你不需要在模板中直接使用响应式数据,则可以使用refreactive。如果你要创建的是简单类型的数据,比如数字、字符串、布尔值等,那么使用 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.stringifyJSON.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.stringifyJSON.parse或者Object.assign操作时,避免丢失响应式属性,让你的代码更加健壮和可维护。