目錄
一、?插值表達式
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 }) // ? 正確