defineComponent:
响应式基础:
ref
在组合式 API 中,推荐使用 ref() 函数来声明响应式状态: ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象
中返回:
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
ref 的自动解包
顶级的 ref 属性
在模板中会被自动“解包〞,所以不需要使用 .value。
const count = ref(0)
const object = { id: ref(1) }
// count 和 object 是顶级属性,但 object.id 不是:
// 我们可以将 id 解构为一个顶级属性:
const { id } = object
{{id + 1}} // 响应式生效
如果ref作为文本的最终计算值(ref在中
),依然会被解包
// ts
const object = { id: ref(1) }
// template 这里没有多余的加1
{{ object.id }} // => 在{{}}中, object.id 值是ref,自动解包
为什么要使用ref?
为什么我们需要使用带有 .value 的 ref,而不是普通的变量? 检测普通变量的访问或修改是行不通的。然而,我们可以通过 getter 和 setter 方法来拦截对象属性的 get 和 set 操作
.value 属性
给予了 Vue 一个机会来检测 ref
何时被访问或修改
// 伪代码,不是真正的实现
const myRef = {
_value: 0,
get value() {
track()
return this._value
},
set value(newValue) {
this._value = newValue
trigger()
}
}
深层响应式
Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map
import { ref } from 'vue'
const obj = ref({
nested: { count: 0 },
arr: ['foo', 'bar']
})
function mutateDeeply() {
// 以下都会按照期望工作
obj.value.nested.count++
obj.value.arr.push('baz')
}
reactive
与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
Vue 能够拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新。 reactive() 将深层地转换对象:当访问嵌套对象时,它们也会被 reactive() 包装
proxy代理
reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的
const raw = {}
const proxy = reactive(raw)
// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false
最佳实践: 是仅使用你声明对象的代理版本。
保证代理一致性
// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true
// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true
reactive局限性
- 有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型
- 不能替换整个对象:引用的响应式连接将丢失
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解包细节
ref作为reactive对象属性
一个ref 会在作为响应式对象的属性被访问或修改时自动解包
。换句话说,它的行为就像一个普通的属性:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
ref赋值给reactive的ref属性,将会替换
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1
ref在reactive数组集合中不会解包
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)
模板中,顶层ref才解包
count 和 object 是顶级属性,但 object.id 不是
const count = ref(0)
const object = { id: ref(1) }
// 1. 顶层才自动解包
{{ count + 1 }} // ok
{{ object.id + 1 }} // not
// 解决=> 将带有ref属性值的id解构
const {id} = object
{{ id + 1 }} // ok
// 2. 在{{ }} 中,如果ref是最终计算值,自动解包
{{ object.id }} // 没有多余的加1
类型标注
ref
泛型参数
// 在调用 ref() 时传入一个泛型参数
const year = ref<string | number>('2020')
// 得到的类型:Ref<string | number>
year.value = 2020 // 成功!
// 如果指定泛型参数但是没给初始值
// 推导得到的类型:Ref<number | undefined>
const n = ref<number>()
reactive
隐式推导
import { reactive } from 'vue'
// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
接口
不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue'
// 为user标注类型
interface Person {
name: string
age: number
}
export default defineComponent({
name: 'App',
setup() {
// ref标注类型
const count = ref<string | number>(0)
// user:Interface
const user: Person = reactive({
name: 'sunflow',
age: 18
})
const increase = () => {
// 因为为ref标注了类型可能为string,所以这里就需要判断了
if (typeof count.value === 'number') {
count.value++
}
user.age++
}
return {
count,
user,
increase
}
}
})
</script>
<template>
<h1 @click="increase">{{ count }}</h1>
</template>
ref 和 reactive 区别
- ref返回一个包含 .value 属性的响应式对象,常用于基本数据类型(number,string),当然对象也行。
- reactive 函数用于创建对象类型的响应式引用。它会递归地将对象的所有属性转换为响应式属性。可以直接访问属性,不需要额外的
.value
- reactive 参数只能是 object, ref都可以的(包括原始数据类型 string, boolean,number.)
- ref需要使用 value 来访问其中的值,reactive 不需要。
- ref 在 Vue 源代码内部是一种特殊的 reactive
// ref相当于这样一个模型
const myRef = reactive({
value:"I'm ref!"
})
myRef.value // "I'm ref!"
myRef.value = "Changed"
myRef.value // "Changed"
怎么选择? 没有特殊的规则,按照个人喜好。 个人喜欢原始类型使用 ref
, object 使用 reactive
。
BugFix:
- VScode在DOM中打入花括号会出现
{ }}
三个标签的错误
cmd + shift + p 找到用户设置 找到 autoClosingBrackets