在Vue3中,提供reactive和ref响应式数据的API。reactive()方法运用proxy的代理对象,解决大量在Vue2中Object.defineProperty的痛点,更好支持对象监听,但proxy并不能直接监听基础类型,需要每次构建一个对象,在用proxy代理对象就会造成极大性能损耗,因此Vue3就提供ref方法,通过返回一个简单的响应式对象,专门处理基础类型的数据响应。
-
ref
ref函数常用于基础类型。
- ref接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回。
ref函数接受的是一个基本类型的单值,需要将其转换成对象可以通过value来访问,可以使用class类,get语法将对象属性绑定到查询该属性时将被调用的函数 。
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
- 在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们。
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<div>{{ count }}</div>
-
reactive
reactive()
常用来定义对象(或者数组)类型数据,返回的是一个原始对象的Proxy。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性。
import { reactive } from 'vue'
const state = reactive({ count: 0 })
在模板中使用:
<button @click="state.count++">
{{ state.count }}
</button>
reactive()
API 有一些局限性:
有限的值类型:它只能用于对象类型 (对象、数组和如
Map
、Set
这样的集合类型)。它不能持有如string
、number
或boolean
这样的原始类型。不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
let state = reactive({ count: 0 })
// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })
- 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
总结
ref
的局限在于每次访问基础类型值时都需要通过.value,但在处理简单数据时更为直观和高效。
reactive
虽能直接操作对象属性,但对于基础类型则无能为力,且存在不能直接替换整个响应式对象、解构操作可能失去响应性等问题。