前端面经-VUE3篇--vue3基础知识(一)插值表达式、ref、reactive
目录
一、 插值表达式
1、插值表达式 ({{}}) 的本质与作用:
2、与 Vue 响应式系统关系:
二、指令
1、什么是 Vue 指令?
2、指令的分类
1、内置指令
① 内容绑定:v-text 和 v-html
② 属性绑定:v-bind
③ 事件绑定:v-on
④ 条件渲染:v-if、v-else-if、v-else
⑤ 列表渲染:v-for
⑥ 双向绑定:v-model
⑦ 性能优化:v-once、v-memo
⑧ 其他指令
v-pre:跳过模板编译阶段(提升渲染性能)。
v-cloak:防止插值表达式闪烁(未编译完毕时隐藏元素)。
2、自定义指令(用户扩展)
3、javascript表达式
4、动态参数
1、什么是「动态参数」?
2、动态参数值的限制:
3、参数语法的限制:
三、响应式基础
1、ref()
1、 在模板中如何使用?
2、模板 ref(获取DOM元素引用)
3、 为 ref() 标注类型
1、为什么要为 ref() 标注类型?
2、如何为 ref() 标注类型?
方法一:显式标注类型(推荐)
方法二:类型推导(隐式标注)
3、复杂类型如何标注类型?
4、模板 ref 的类型标注
2、reactive()
1、reactive() 基本用法
2、在模板中使用:
3、reactive() 深层响应式特性
4、ref 解包
为什么会存在额外的 ref 解包?
需要注意的问题(⚠️重要)
问题1:在赋值新值时仍保持 ref 特性
问题2:解构赋值问题
数组和集合的注意事项
在模板中解包的注意事项
5、为 reactive 标注类型
方式一:使用接口(Interface)标注类型(推荐)
方式二:使用类型别名(type)标注类型
方式三:显式类型断言(不推荐,偶尔使用)
为复杂嵌套数据标注类型的实践示例
使用泛型标注 reactive 的常见错误
我们知道vue的特点是
-
声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
-
响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
最显著的一个特点就是响应式,那么引出插值表达式
一、 插值表达式
1、插值表达式 ({{}}
) 的本质与作用:
插值表达式是 Vue 模板语法的一部分,它告诉 Vue:
这里需要绑定数据,请自动更新这个区域的内容。<div>{{ message }}</div>
插值用于显示在元素内部的文本内容。
本质上是在告诉 Vue:
-
“请帮我监听响应式数据对象中的
message
字段。” -
“当它的值发生变化时,请自动更新这个
<div>
中显示的内容。”
作用:
-
数据绑定(Data Binding)
-
绑定响应式数据的值,使得视图总能与数据保持同步。
-
-
自动更新 UI
-
数据变化后,Vue 会自动地更新插值表达式对应的 DOM 区域。
-
-
便捷显示数据
-
简单直观地展示动态内容,减少繁琐的 DOM 操作。
-
除了基本变量名,插值表达式内还可以使用简单的 JavaScript 表达式:
<div>{{ count * 2 }}</div>
<div>{{ isLoggedIn ? "已登录" : "未登录" }}</div>
2、与 Vue 响应式系统关系:
插值表达式依靠 Vue 响应式系统:
-
Vue 内部通过
ref
或reactive
等 API 使数据变得响应式。 -
当响应式数据变化,Vue 自动触发页面重新渲染,更新插值内容。
有小伙伴回想Vue 已经响应式了,为何还用插值?
-
Vue 是响应式框架,响应式的含义是数据变化后自动更新视图,而插值表达式 (
{{}}
) 就是这种自动更新机制体现到模板上的一种方式。 -
插值表达式让数据的自动变化直接体现在页面显示上,开发者无需手动编写监听与更新代码。
我们知道了插值表达式以后要注意两点
1、插值表达式无法用在 HTML 属性中 <div class="{{className}}">无效</div>
因此我们等会要学习指令,指令使得我们的元素的属也可以变成响应式
2、 插值表达式仅适合简单逻辑、
-
插值中不要编写过于复杂的表达式。
-
复杂逻辑建议抽离到
computed
或方法中
二、指令
1、什么是 Vue 指令?
在 Vue 中,指令(Directives) 是带有前缀 v-
的特殊属性,用于在模板中提供特定的行为或功能。
Vue 的指令本质上是:
-
一种模板语法扩展。
-
用来在模板中声明式地控制 DOM 的行为、渲染逻辑以及响应式交互。
2、指令的分类
Vue 指令分为:
-
内置指令(Vue 自带):如
v-if
、v-for
等。 -
自定义指令(用户定义):可根据需求自定义,如:
v-focus
。
1、内置指令
① 内容绑定:v-text
和 v-html
v-text
:更新元素的文本内容,与插值表达式作用类似,但会覆盖元素内所有内容。
<div v-text="message"></div>
<!-- 相当于 -->
<div>{{ message }}</div>
v-html
:输出原始 HTML 内容(会解析HTML标签)。
插值表达式会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用v-html指令
假如你的数据是这样的:
data() {return {rawHtml: '<span style="color: red">这是红色字体!</span>'}
}
使用插值表达式 ({{ }}
):
<!-- 普通插值会直接输出HTML代码,而非解析它 -->
<div>{{ rawHtml }}</div>
页面上会直接显示:<span style="color: red">这是红色字体!</span>
(HTML标签会显示成原样,而不会解析为真正的HTML元素)
使用 v-html
指令:
<div v-html="rawHtml"></div>
页面实际渲染结果:
<div><span style="color: red">这是红色字体!</span>
</div>
这次页面会真正地把字符串解析为HTML元素,显示为:
这是红色字体!(字体颜色为红色)
使用 v-html
时必须特别注意安全问题:
-
Vue 使用
v-html
时会解析 HTML 字符串,但不会对内容进行安全过滤。 -
如果插入了用户提供的不可信内容(如用户留言、评论等),可能引发 XSS(跨站脚本攻击)风险。
② 属性绑定:v-bind
作用:动态绑定元素的属性或组件的 props。
<!-- 完整写法 -->
<a v-bind:href="link">链接</a>
<!-- 简写 -->
<a :href="link">链接</a>
常用场景:动态class绑定、style绑定、组件传值。
<div :class="{ active: isActive }"></div>
③ 事件绑定:v-on
作用:为元素绑定事件监听器,如 click、submit 等。
<!-- 完整写法 -->
<button v-on:click="doSomething">按钮</button>
<!-- 简写 -->
<button @click="doSomething">按钮</button>
-
事件修饰符(常用):
修饰符 | 作用 |
---|---|
.stop | 阻止事件冒泡 |
.prevent | 阻止默认事件 |
.once | 只触发一次 |
.capture | 事件捕获模式 |
④ 条件渲染:v-if
、v-else-if
、v-else
根据条件渲染元素:
<div v-if="isActive">活跃状态</div>
<div v-else>未活跃状态</div>
注意:v-if
会完全删除或重建 DOM 节点。
补充:v-show
-
v-show
仅控制元素的display
属性,不删除 DOM。 -
适用于频繁切换场景。
<div v-show="isVisible">显示内容</div>
⑤ 列表渲染:v-for
循环遍历数组或对象:必须指定 key
属性来优化更新效率。
<ul><li v-for="(item, index) in items" :key="item.id">{{ index }} - {{ item.name }}</li>
</ul>
⑥ 双向绑定:v-model
-
将表单控件与数据自动双向绑定,数据和视图实时同步:
<input v-model="username" placeholder="请输入用户名">
-
常用修饰符:
修饰符 | 作用 |
---|---|
.lazy | 在 input 失去焦点或回车时才同步 |
.number | 将输入转为数值类型 |
.trim | 自动过滤首尾空白字符 |
<input v-model.number="age">
⑦ 性能优化:v-once
、v-memo
-
v-once
:只渲染一次,不再响应后续数据变化:<span v-once>{{ staticContent }}</span> -
v-memo
(Vue 3.2+):精确控制渲染,只有依赖的值变化时才重新渲染:<div v-memo="[key]">{{ key }}</div>
⑧ 其他指令
-
v-pre
:跳过模板编译阶段(提升渲染性能)。
<span v-pre>{{ raw }}</span> <!-- 直接显示 {{ raw }} -->
v-cloak
:防止插值表达式闪烁(未编译完毕时隐藏元素)。
<style>
[v-cloak] { display: none; }
</style>
<div v-cloak>{{ msg }}</div>
2、自定义指令(用户扩展)
当内置指令不能满足需求时,用户可以创建自定义指令。
注册示例(Vue 3.x):
const app = Vue.createApp({})app.directive('focus', {mounted(el) {el.focus()}
})
使用自定义指令:
<input v-focus>
指令的生命周期钩子(自定义指令专用)
钩子函数 | 描述 |
---|---|
created | 指令绑定元素后调用 |
beforeMount | 元素插入到 DOM 前调用 |
mounted | 元素插入到 DOM 后调用 |
beforeUpdate | 更新前调用 |
updated | 更新后调用 |
beforeUnmount | 元素卸载前调用 |
unmounted | 元素卸载后调用 |
3、javascript表达式
在 Vue 中,模板语法本质上是基于 JavaScript 表达式的。
也就是说:
-
在 Vue 模板的插值表达式 (
{{}}
) 或指令(如v-bind
、v-if
、v-for
)中,实际填写的内容本质上都是 JavaScript 表达式。 -
Vue 在渲染模板时会自动对这些 JavaScript 表达式进行求值,并将结果渲染到视图上。
JavaScript表达式 (Expression) 是一种产生值的代码片段。它的特点是:
-
任何能返回一个值的语句都是表达式。
-
表达式求值后一定会得到一个具体的值。
-
表达式本身可以由:
-
字面量(Literal) 如数字、字符串。
-
变量引用(如:
x
)。 -
运算符组合(如:
a + b
)。 -
函数调用(如:
getName()
)。 -
三元运算符(如:
x > 1 ? 'Yes' : 'No'
)等组成。
-
// 字面量
42; // 数字表达式
"Hello"; // 字符串表达式// 算术表达式
1 + 2; // 求值为 3// 逻辑表达式
a > b && c < d; // 返回 true 或 false// 三元表达式
isActive ? '是' : '否';// 函数调用表达式
getUsername(); // 返回调用函数的返回值
在 Vue 模板中只能使用表达式,不能使用语句。
<!-- 无效,会报错 -->
<div>{{ if(a > b) { return a; } }}</div>
4、动态参数
1、什么是「动态参数」?
传统绑定是静态的(固定属性名):<a :href="url">链接</a>
动态参数的作用是,允许动态决定绑定哪个属性:
<a :[attributeName]="url">链接</a>
此时:
-
[attributeName]
是一个动态参数。 -
它的实际值由
attributeName
这个数据属性的值决定。
2、动态参数值的限制:
动态参数表达式的值,必须是字符串或 null
,其他类型的值(如数字、对象、布尔值)会报错。
3、参数语法的限制:
-
动态参数内的表达式只允许简单表达式,不允许复杂的 JavaScript 语句。
-
比如不能直接写三元表达式、函数调用、对象属性访问等复杂逻辑。
示例:假设有个按钮组件,动态绑定事件
<template><button @[eventType]="onTrigger">触发按钮</button>
</template><script setup>
import { ref } from 'vue'const eventType = ref('click') // 可修改为其他事件如 mouseover、dblclickfunction onTrigger() {alert('事件触发!')
}
</script>
-
动态决定绑定的事件类型。
-
极大增加组件的可复用性和灵活性。
三、响应式基础
1、ref()
ref()
是 Vue 3 中一个重要的响应式 API,专门用来创建响应式数据。它的全称为 reference(引用),作用是将一个基本类型数据或对象包裹成响应式对象,使数据能够在发生变化时自动通知视图更新。
特点:
-
可用来处理任何类型(基本类型、对象、数组)。
-
通常更常用于基本类型数据(例如数字、字符串、布尔值)。
-
访问数据时需通过
.value
属性。
import { ref } from 'vue'
const count = ref(0) // 创建一个响应式数据,初始值为 0
访问数据时:console.log(count.value) // 输出 0
修改数据:count.value++
console.log(count.value) // 输出 1
⚠️ 注意:
ref()
返回的是一个对象,所以必须使用 .value
来访问真正的值。
1、 在模板中如何使用?
在模板中,Vue 自动帮你解包 .value
:
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {count.value++
}
</script>
<template><div>{{ count }}</div> <!-- 这里不需要写 count.value,模板会自动帮你解包 --><button @click="increment">+1</button>
</template>
-
模板中:直接使用
count
即可。 -
JavaScript 中:需要
count.value
。
ref 如何处理对象类型?
虽然推荐使用 ref()
来处理基本类型数据,但也可以用它创建对象或数组:
const obj = ref({ name: 'Tom', age: 20 })
console.log(obj.value.name) // Tom
obj.value.age = 21 // 依然是响应式更新
但注意:
-
使用
ref
创建的对象类型数据本质上是在 ref 中存储一个 reactive 对象。 -
若直接替换对象时必须用
.value
:
// 正确方式:
obj.value = { name: 'Jerry', age: 22 }
⚠️ 若想避免频繁使用 .value
,可用 reactive
替代:
const obj = reactive({ name: 'Tom', age: 20 })
2、模板 ref(获取DOM元素引用)
在模板里使用 ref
属性获取 DOM 元素或子组件的引用:
<template><input ref="inputRef"><button @click="focusInput">聚焦输入框</button>
</template><script setup>
import { ref } from 'vue'const inputRef = ref(null)function focusInput() {inputRef.value.focus() // 获取到真实的DOM元素并调用方法
}
</script>
-
inputRef
会绑定到对应的DOM或子组件实例上。 -
通常用于直接操作DOM或子组件(如:聚焦、调用子组件方法)。
3、 为 ref()
标注类型
1、为什么要为 ref()
标注类型?
默认情况下,Vue 3 已经提供了对 TypeScript 的完整支持,但在使用 ref()
创建响应式数据时,明确地标注类型会带来更多的好处:
-
提升代码可读性:
-
其他开发者一看就知道你的数据类型是什么。
-
-
获得更好的类型检查:
-
IDE 能精准识别类型错误,避免潜在Bug。
-
-
IDE 更好的自动补全支持:
-
更流畅的开发体验,提高效率。
-
2、如何为 ref()
标注类型?
方法一:显式标注类型(推荐)
使用泛型明确指定类型:
import { ref } from 'vue'// 标注为数字类型的ref
const count = ref<number>(0)// 标注为字符串类型的ref
const name = ref<string>('Vue')// 标注为对象类型的ref
interface User {name: stringage: number
}const user = ref<User>({name: 'Tom',age: 30
})
方法二:类型推导(隐式标注)
如果你没有显式指定泛型类型,Vue 会根据初始值自动推导类型:
import { ref } from 'vue'// 自动推断 count 为 number 类型的 Ref<number>
const count = ref(0)// 自动推断 name 为 string 类型的 Ref<string>
const name = ref('Vue')
但注意:
-
如果初始值是
null
或undefined
,Vue 会推导成宽泛类型Ref<null>
或Ref<undefined>
,这通常不是我们想要的:
const name = ref(null) // 推断为 Ref<null>,一般意义不大
这种情况建议你显式标注类型:
// 更好的写法:
const name = ref<string | null>(null)
3、复杂类型如何标注类型?
常用接口或类型别名定义复杂对象,再传给 ref()
:
接口类型示例:
interface Article {title: stringcontent: stringlikes: number
}// 显式指定 Article 类型
const article = ref<Article>({title: 'Vue 3 指南',content: 'Vue 3 的核心...',likes: 0
})
类型别名示例:
type Status = 'success' | 'error' | 'loading'const status = ref<Status>('loading')
4、模板 ref 的类型标注
模板 ref 是指用于绑定到 DOM 元素或组件引用的 ref
:
<template><input ref="inputRef" />
</template><script setup lang="ts">
import { ref, onMounted } from 'vue'// HTMLInputElement 标注 DOM元素类型
const inputRef = ref<HTMLInputElement | null>(null)onMounted(() => {inputRef.value?.focus()
})
</script>
-
inputRef
必须初始化为null
,因为挂载前没有元素。 -
需要标注为
HTMLElement
或具体的 DOM 元素类型,如HTMLInputElement
。
4、与Ref的关系
Ref
是一个TypeScript 类型接口:
-
它用于明确地描述由
ref()
函数返回的对象的类型结构。 -
其定义为:
Ref
是泛型接口,通过泛型参数指定存储的数据类型。
interface Ref<T> {value: T
}
使用示例:
import { Ref, ref } from 'vue'// 显式指定count的类型为Ref<number>
const count: Ref<number> = ref(0)// 复杂类型示例:
interface User {name: stringage: number
}// 显式指定类型Ref<User>
const user: Ref<User> = ref({name: 'Vue',age: 3
})
两者之间的关系是实现与类型声明的关系:
名称 | 类型 | 用途 |
---|---|---|
ref() | 函数 | 创建一个响应式的数据对象 |
Ref | TypeScript 泛型接口 | 描述 ref() 返回的数据的类型结构 |
2、reactive()
在 Vue 3 中:
-
reactive()
是一个核心的 API,用于创建一个响应式对象或数组。 -
当对象或数组的数据发生变化时,Vue 会自动更新使用该数据的视图。
本质上:
-
reactive()
会将一个普通 JavaScript 对象或数组转换为响应式代理对象(Proxy)。 -
所有对该对象属性的读取、修改操作都会被拦截,从而实现响应式更新。
1、reactive()
基本用法
import { reactive } from 'vue'
// 创建一个响应式对象
const state = reactive({name: 'Vue',count: 0
})
使用响应式数据:
console.log(state.name) // 输出:Vue
state.count++ // 数据更新,自动触发视图更新
2、在模板中使用:
在模板中,reactive()
创建的对象属性能直接访问:
<script setup>
import { reactive } from 'vue'const state = reactive({ name: 'Tom', age: 30 })function updateAge() {state.age++
}
</script><template><div>{{ state.name }} - 年龄: {{ state.age }}</div><button @click="updateAge">增加年龄</button>
</template>
注意:
-
和
ref()
不同,reactive()
创建的数据访问时不需要.value
。
reactive vs ref 区别对比
特性 | reactive() | ref() |
---|---|---|
适用类型 | 对象或数组 | 基础数据类型(单个值) |
访问方式 | 不需要 .value | 必须 .value |
本质 | Proxy代理对象 | 包裹基本值的响应式对象 |
适用场景 | 复杂对象、数组 | 单一基本类型数据 |
3、reactive()
深层响应式特性
在 Vue 3 中:
-
使用
reactive()
创建的响应式对象默认都是深层响应式的。 -
深层响应式意味着:
-
如果一个对象中嵌套了其他对象或数组,这些嵌套的对象或数组也会自动成为响应式。
-
不论嵌套对象有多少层,修改任何一层中的属性,都会自动触发视图的更新。
-
简单理解为:
reactive() 会递归地将整个对象树都变为响应式。
假设我们创建一个嵌套的对象:
import { reactive } from 'vue'const state = reactive({user: {name: 'Tom',info: {age: 20,hobbies: ['篮球', '音乐']}}
})
这时,state
整个对象树都是响应式的:
state.user.name = 'Jerry' // ✔️ 响应式更新
state.user.info.age = 21 // ✔️ 深层响应式更新
state.user.info.hobbies.push('阅读') // ✔️ 数组响应式更新
-
不需要额外手动声明嵌套数据为响应式。
-
Vue 会自动递归 Proxy 化整个嵌套结构。
深层响应式的内部原理(Proxy递归机制)
深层响应式的实现,背后的机制是 JavaScript 的 Proxy 对象:
简化版原理:
function reactive(target) {return new Proxy(target, {get(target, key, receiver) {const res = Reflect.get(target, key, receiver)// 若获取到的值是对象,则递归 Proxy 化,形成深层响应式if (typeof res === 'object' && res !== null) {return reactive(res)}// 依赖收集track(target, key)return res},set(target, key, value, receiver) {const result = Reflect.set(target, key, value, receiver)// 触发更新通知trigger(target, key)return result}})
}
-
当访问对象属性(
get
)时,若是嵌套对象,则会自动再次调用reactive()
创建深层 Proxy。 -
这样确保所有嵌套对象都实现响应式。
state.user.address.city = '北京' // ✔️ 自动响应式
深层响应式 vs 浅层响应式
为了应对某些特定场景,Vue 也提供了浅层响应式(Shallow Reactive):
API | 功能 | 深层响应式 |
---|---|---|
reactive() | 创建响应式对象 | ✅ 深层响应式 |
shallowReactive() | 创建浅层响应式对象 | ❌ 只响应顶层 |
import { shallowReactive } from 'vue'const state = shallowReactive({user: { name: 'Tom', age: 20 }
})state.user.age = 21 // ❌ 不会触发响应式更新
state.user = { name: 'Jerry', age: 22 } // ✔️ 会触发响应式更新(只监听顶层属性)
reactive()
与解构的注意事项
reactive()
创建的响应式对象若直接解构,会导致数据丢失响应性:
const state = reactive({ count: 0 })// 解构后 count 非响应式!
let { count } = statecount++ // 无法触发响应式更新!
正确方式(toRefs):
使用 Vue 提供的 toRefs()
进行解构,保持响应式:
import { reactive, toRefs } from 'vue'const state = reactive({ count: 0 })const { count } = toRefs(state) // 转换为 ref 类型,保持响应式count.value++ // ✔️ 仍然响应式
4、ref 解包
在 Vue 3 的响应式系统中,存在一种特别的机制称为 Ref 解包(Ref Unwrapping):
-
当你使用
ref()
创建响应式数据时,数据被包裹在一个具有.value
属性的对象里。 -
在模板中使用这些数据时,Vue 会自动帮你去掉
.value
,直接访问真正的数据,这个过程叫做自动解包(Automatic Unwrapping)。
然而,除了模板中的自动解包外,Vue 还在一些特定场景中提供了 额外的 ref 解包机制:
-
即在一些特殊的响应式对象中(例如用
reactive()
创建的对象),如果它的属性值是一个ref()
,则访问这个属性时,Vue 会自动解包到.value
。
看一个直观的例子:
import { ref, reactive } from 'vue'const count = ref(1)// reactive 对象包含一个 ref
const state = reactive({countRef: count
})// 访问 state.countRef 时,不需要 .value,也能自动解包:
console.log(state.countRef) // 输出:1,而不是 { value: 1 }
state.countRef
本质上是 ref
对象,但 Vue 的响应式代理会自动解包,使你无需显式使用 .value
访问。
为什么会存在额外的 ref 解包?
额外的 ref 解包主要是为了:
-
让开发者在使用嵌套响应式数据时更方便、直观,不必在模板外过多考虑
.value
的存在。 -
特别是在构建复杂的响应式数据结构时,使代码更加简洁、直观。
如果没有这个机制:
-
使用 reactive 对象嵌套 ref 时,每次访问内部值都必须手动使用
.value
,非常麻烦。
适用场景
reactive()
对象的属性是 ref()
:
const nameRef = ref('Tom')const state = reactive({ name: nameRef })console.log(state.name) // 自动解包为 'Tom'
需要注意的问题(⚠️重要)
问题1:在赋值新值时仍保持 ref 特性
const countRef = ref(0)
const state = reactive({ count: countRef })state.count = 10 // 这里本质上相当于 countRef.value = 10
console.log(countRef.value) // 10(依然联动变化)
问题2:解构赋值问题
-
如果你直接对 reactive 对象解构,则会失去响应性:
数组和集合的注意事项
与 reactive 对象不同的是,当 ref 作为响应式数组或原生集合类型 (如 Map
) 中的元素被访问时,它不会被解包:
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) }
//因此,这个表达式按预期工作:
{{ count + 1 }}
//但这个不会:
{{ object.id + 1 }}
//渲染的结果将是 [object Object]1,因为在计算表达式时 object.id 没有被解包,仍然是一个 ref 对象。
//为了解决这个问题,我们可以将 id 解构为一个顶级属性:
const { id } = object
{{ id + 1 }} //渲染的结果将是 2//注意
//一个需要注意的点是,如果 ref 是文本插值的最终计算值 (即 {{ }} 标签),那么它将被解包,因此以下内容将渲染为 1:
{{ object.id }}
5、为 reactive 标注类型
为 reactive()
标注类型通常采用以下几种方式:
方式一:使用接口(Interface)标注类型(推荐)
接口是一种定义复杂对象结构的最佳方式:
import { reactive } from 'vue'interface User {name: stringage: number
}// 明确标注 reactive 对象的类型为 User
const user = reactive<User>({name: 'Tom',age: 30
})
方式二:使用类型别名(type)标注类型
类型别名用法与接口类似,适合定义简单或复合类型:
import { reactive } from 'vue'type Status = 'success' | 'error' | 'loading'interface ResponseData {status: Statusdata: string[]
}// 使用类型别名与接口结合
const response = reactive<ResponseData>({status: 'loading',data: []
})
方式三:显式类型断言(不推荐,偶尔使用)
显式类型断言能快速定义类型,但会牺牲类型检查严谨性,一般不推荐:
import { reactive } from 'vue'const state = reactive({name: 'Vue',version: 3
} as { name: string; version: number })
⚠️ 注意:
-
类型断言会略过类型校验,可能隐藏错误。
为复杂嵌套数据标注类型的实践示例
示例:
import { reactive } from 'vue'interface Address {city: stringzipCode: string
}interface User {name: stringaddress: Address
}const user = reactive<User>({name: 'Jerry',address: {city: 'Shanghai',zipCode: '200000'}
})
-
明确的类型标注帮助 IDE 提供更精确的属性提示。
使用泛型标注 reactive 的常见错误
有些开发者可能尝试这样做(但这是错误的):
const user: User = reactive({ name: 'Tom', age: 20 }) // ❌ 错误!
-
reactive()
返回的类型并非User
本身,而是User
类型的 Proxy 代理。 -
正确的写法是用泛型指定:
const user = reactive<User>({ name: 'Tom', age: 20 }) // ✅ 正确