EP22 - 屬性

更新於 發佈於 閱讀時間約 1 分鐘
這一頁假設你已經閱讀了組件基礎。如果你對組件還不熟悉,請先閱讀那部分內容。 Props屬性是 Vue 組件中一個非常重要的概念,用來接收從父組件傳遞過來的數據。使用 props 可以讓組件更加靈活和可重用。慢慢對他有點熟悉了吧!

Prop 定義 - Props Declaration​

Vue 組件需要明確的 prop 定義,這樣 Vue 才能知道應該將傳遞給組件的哪些外部 props 視為透傳屬性 fallthrough attributes(這部分內容將在專門的部分討論)。

在使用 <script setup> 的單文件組件(SFC)中,可以使用 defineProps() 巨集來定義 props:

<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>

在非 <script setup> 的組件中,props 是使用 props 選項來定義的:

export default {
props: ['foo'],
setup(props) {
// setup() 接收的第一個參數就是 props。
console.log(props.foo)
}
}

注意傳遞給 defineProps() 的參數與提供給 props 選項的值相同:這兩種定義方式共享相同的 props 選項 API。

除了使用字符串數組定義 props 外,我們還可以使用物件語法:

// 在 <script setup> 中
defineProps({
title: String,
likes: Number
})
// 在非 <script setup> 中
export default {
props: {
title: String,
likes: Number
}
}

在物件定義語法中,每個屬性的鍵是 prop 的名字,而值應該是預期類型的建構函數。

這不僅能記錄你的組件,還能在其他開發者在瀏覽器控制台中傳遞錯誤類型時給出警告。關於 prop 驗證的更多細節,我們將在本頁面後面討論。

如果你在 <script setup> 中使用 TypeScript,也可以使用純類型註釋來定義 props:

<script setup lang="ts">
defineProps<{
title? : string
likes?: number
}>()
</script>

更多細節: Typing Component Props

可響應的 Props 解構 - Reactive Props Destructure (+3.5)

Vue 的響應系統根據屬性存取來追蹤狀態使用。例如,當你在計算屬性或監聽器中存取 props.foo 時,foo prop 會被追蹤為一個依賴。

例如,給定以下程式碼:

const { foo } = defineProps(['foo'])

watchEffect(() => {
// 在 3.5 之前只執行一次
// 在 3.5 之後當 "foo" prop 變更時重新執行
console.log(foo)
})

在 3.4 及更低版本中,foo 是一個實際的常量,永遠不會變更。在 3.5 及更高版本中,Vue 的編譯器會在同一 <script setup> 區塊中存取從 defineProps 解構的變數時,自動添加 props. 前綴。因此,以上程式碼相當於以下程式碼:

const props = defineProps(['foo'])

watchEffect(() => {
// `foo` 被編譯器轉換為 `props.foo`
console.log(props.foo)
})

此外,你可以使用 JavaScript 的原生預設值語法為 props 宣告預設值。這在使用基於類型的 props 定義時特別有用:

const { foo = 'hello' } = defineProps<{ foo?: string }>()

如果你希望在 IDE 中更明顯地區分解構的 props 和普通變數,Vue 的 VSCode 擴充提供了一個設定,可以啟用解構 props 的內嵌提示。

將解構的 Props 傳入函數

當我們將解構的 prop 傳入函數時,例如:

const { foo } = defineProps(['foo'])

watch(foo, /* ... */)

這將無法如預期運作,因為它相當於 watch(props.foo, ...) ,我們傳遞的是一個值而不是一個響應式資料源給 watch。事實上,Vue 的編譯器會捕捉這種情況並拋出警告。

類似於我們可以使用 watch(() => props.foo, ...) 來監聽普通的 prop,我們也可以通過將解構的 prop 包裝在 getter 中來監聽:

watch(() => foo, /* ... */)

此外,當我們需要將解構的 prop 傳入外部函數並保留響應性時,這是推薦的方法:

useComposable(() => foo)

外部函數可以在需要追蹤提供的 prop 變化時調用 getter(或使用 toValue 進行標準化),例如在計算屬性或監聽器的 getter 中。

Prop 傳遞詳情 - Prop Passing Details

Prop 名稱大小寫

我們使用 camelCase 來宣告較長的 prop 名稱,因為這樣可以避免在使用它們作為屬性鍵時需要加上引號,並且允許我們在模板表達式中直接引用它們,因為它們是有效的 JavaScript 標識符:

defineProps({
greetingMessage: String
})
<span>{{ greetingMessage }}</span>

技術上來說,當傳遞 props 給子組件時(除非在 DOM 模板中),你也可以使用 camelCase。然而,慣例是在所有情況下都使用 kebab-case,以對齊 HTML 屬性:

<MyComponent greeting-message="hello" />

我們盡可能使用 PascalCase 來命名組件標籤,因為這樣可以通過區分 Vue 組件和原生元素來提高模板的可讀性。然而,在傳遞 props 時使用 camelCase 並沒有太大的實際好處,所以我們選擇遵循每種語言的慣例。

靜態 vs. 動態 Props

到目前為止,你已經看到了作為靜態值傳遞的 props,例如:

<BlogPost title="My journey with Vue" />

你也看到了使用 v-bind 或其 : 簡寫來動態賦值的 props,例如:

<!-- 動態賦值變量的值 -->
<BlogPost :title="post.title" />

<!-- 動態賦值複雜表達式的值 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

傳遞不同類型的值

在上面的兩個例子中,我們碰巧傳遞的是字符串值,但可以傳遞給 prop 的值類型不限於此。

數字

<!-- 即使 `42` 是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :likes="42" />

<!-- 動態賦值變量的值。 -->
<BlogPost :likes="post.likes" />

布林值

<!-- 包含沒有值的 prop 將默認為 `true`-->
<BlogPost is-published />

<!-- 即使 `false` 是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :is-published="false" />

<!-- 動態賦值變量的值。 -->
<BlogPost :is-published="post.isPublished" />

陣列

<!-- 即使陣列是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- 動態賦值變量的值。 -->
<BlogPost :comment-ids="post.commentIds" />

物件

<!-- 即使物件是靜態的,我們也需要 v-bind 來告訴 Vue 這是 JavaScript 表達式,而不是字符串。 -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>

<!-- 動態賦值變量的值。 -->
<BlogPost :author="post.author" />

使用物件綁定多個屬性

如果你想將物件的所有屬性作為 props 傳遞,你可以使用不帶參數的 v-bind(即 v-bind 而不是 :prop-name)。例如,給定一個 post 物件:

const post = {
id: 1,
title: 'My Journey with Vue'
}

以下模板:

<BlogPost v-bind="post" />

將等同於:

<BlogPost :id="post.id" :title="post.title" />

單向數據流 - One-Way Data Flow​

所有 props 形成從父屬性到子屬性的單向綁定:當父屬性更新時,它會向下傳遞到子屬性,但不會反過來。這樣可以防止子組件意外修改父屬性狀態,從而使應用程序的數據流更容易理解。

此外,每當父組件更新時,子組件中的所有 props 都將刷新為最新的值。這意味著你不應該嘗試在子組件中修改 prop。如果你這樣做,Vue 會在控制台中發出警告:

const props = defineProps(['foo'])

// ❌ 警告,props 是只讀的!
props.foo = 'bar'

通常有兩種情況會讓你想要修改 prop:

  1. Prop 用於傳入初始值;子組件希望之後將其用作本地數據屬性。在這種情況下,最好定義一個本地數據屬性,使用 prop 作為其初始值:
    const props = defineProps(['initialCounter'])

    // counter 只使用 props.initialCounter 作為初始值;
    // 它與未來的 prop 更新斷開連接。
    const counter = ref(props.initialCounter)
  2. Prop 作為需要轉換的原始值傳入。在這種情況下,最好定義一個計算屬性,使用 prop 的值:
const props = defineProps(['size'])

// 當 prop 變更時自動更新的計算屬性
const normalizedSize = computed(() => props.size.trim().toLowerCase())

修改物件 / 陣列 Props

當物件和陣列作為 props 傳遞時,雖然子組件不能修改 prop 綁定,但它可以修改物件或陣列的嵌套屬性。這是因為在 JavaScript 中,物件和陣列是通過引用傳遞的,而 Vue 阻止這種修改代價過高。

這種修改的主要缺點是,它允許子組件以父組件不明顯的方式影響父狀態,可能使未來的數據流變得更難理解。作為最佳實踐,你應該避免這種修改,除非父子組件是設計上緊密耦合的。在大多數情況下,子組件應該發出一個事件,讓父組件進行修改。

Prop 驗證 - Prop Validation​

組件可以為它們的 props 指定需求,例如你已經看到的類型。如果不符合需求,Vue 會在瀏覽器的 JavaScript 控制台中發出警告。這在開發要被其他人使用的組件時尤其有用。

要指定 prop 驗證,你可以向 defineProps() 巨集提供一個帶有驗證需求的物件,而不是一個字符串數組。例如:

defineProps({
// 基本類型檢查
// (`null` 和 `undefined` 值將允許任何類型)
propA: Number,
// 多種可能的類型
propB: [String, Number],
// 必需的字符串
propC: {
type: String,
required: true
},
// 必需但可為空的字符串
propD: {
type: [String, null],
required: true
},
// 有默認值的數字
propE: {
type: Number,
default: 100
},
// 有默認值的物件
propF: {
type: Object,
// 物件或數組的默認值必須從工廠函數返回。該函數接收組件接收到的原始 props 作為參數。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定義驗證函數
// 在 3.4+ 中,完整的 props 作為第二個參數傳遞
propG: {
validator(value, props) {
// 值必須匹配這些字符串之一
return ['success', 'warning', 'danger'].includes(value)
}
},
// 有默認值的函數
propH: {
type: Function,
// 不像物件或數組默認值,這不是工廠函數——這是一個用作默認值的函數
default() {
return 'Default function'
}
}
})

提示

defineProps() 參數內的代碼無法訪問 <script setup> 中聲明的其他變數,因為整個表達式在編譯時被移動到外部函數作用域。

額外詳情:

  • 所有 props 默認都是可選的,除非指定了 required: true
  • 除了 Boolean 之外,缺失的可選 prop 將具有 undefined 值。
  • 缺失的 Boolean props 將被轉換為 false。你可以通過為其設置默認值來更改這一點——即:default: undefined 使其表現為非 Boolean prop。
  • 如果指定了默認值,當解析出的 prop 值為 undefined 時將使用它——這包括 prop 缺失或傳遞了明確的 undefined 值。
  • 當 prop 驗證失敗時,Vue 將產生控制台警告(如果使用開發版本)。
  • 如果使用基於類型的 props 聲明,Vue 將盡力將類型註釋編譯為等效的運行時 prop 聲明。例如,defineProps<{ msg: string }> 將編譯為 { msg: { type: String, required: true }}

運行時類型檢查

類型可以是以下本地構造函數之一:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • Error

此外,類型還可以是一個自定義類或構造函數,並且將使用 instanceof 檢查進行斷言。例如,給定以下類:

class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}

你可以將其用作 prop 的類型:

defineProps({
author: Person
})

Vue 將使用 instanceof Person 來驗證 author prop 的值是否確實是 Person 類的實例。

可為空的類型

如果類型是必需但可為空的,你可以使用包含 null 的數組語法:

defineProps({
id: {
type: [String, null],
required: true
}
})

請注意,如果類型只是 null 而不使用數組語法,它將允許任何類型。

布林值轉換 - Boolean Casting​

具有布林(Boolean)類型的 props 具有特殊的轉換規則,以模擬本地布林屬性的行為。假設有一個具有以下聲明的 <MyComponent>

defineProps({
disabled: Boolean
})

組件可以這樣使用:

<!-- 等同於傳遞 :disabled="true" -->
<MyComponent disabled />

<!-- 等同於傳遞 :disabled="false" -->
<MyComponent />

當 prop 被聲明為允許多種類型時,也會應用布林值的轉換規則。但是,當同時允許字符串(String)和布林值時,有一個邊界情況——只有當布林值出現在字符串之前時才會應用布林值的轉換規則:

// disabled 會被轉換為 true
defineProps({
disabled: [Boolean, Number]
})

// disabled 會被轉換為 true
defineProps({
disabled: [Boolean, String]
})

// disabled 會被轉換為 true
defineProps({
disabled: [Number, Boolean]
})

// disabled 會被解析為空字符串 (disabled="")
defineProps({
disabled: [String, Boolean]
})
老實說!這篇文章我是邊看邊睡覺~哈
理解力太差 www ~~ 小睡一會!再回來看看應該會比較懂?
懂原理,但是細節有點多的時候~真的很想睡www~
留言
avatar-img
留言分享你的想法!
avatar-img
卡關的人生
2會員
73內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
透過蝦皮分潤計畫,輕鬆賺取零用金!本文分享5-6月實測心得,包含數據流程、實際收入、平臺優點及注意事項,並推薦高分潤商品,教你如何運用空閒時間創造被動收入。
Thumbnail
透過蝦皮分潤計畫,輕鬆賺取零用金!本文分享5-6月實測心得,包含數據流程、實際收入、平臺優點及注意事項,並推薦高分潤商品,教你如何運用空閒時間創造被動收入。
Thumbnail
單身的人有些會養寵物,而我養植物。畢竟寵物離世會傷心,植物沒養好再接再厲就好了~(笑)
Thumbnail
單身的人有些會養寵物,而我養植物。畢竟寵物離世會傷心,植物沒養好再接再厲就好了~(笑)
Thumbnail
不知你有沒有過這種經驗?衛生紙只剩最後一包、洗衣精倒不出來,或電池突然沒電。這次一次補貨,從電池、衛生紙到洗衣精,還順便分享使用心得。更棒的是,搭配蝦皮分潤計畫,愛用品不僅自己用得安心,分享給朋友還能賺回饋。立即使用推薦碼 X5Q344E,輕鬆上手,隨時隨地賺取分潤!
Thumbnail
不知你有沒有過這種經驗?衛生紙只剩最後一包、洗衣精倒不出來,或電池突然沒電。這次一次補貨,從電池、衛生紙到洗衣精,還順便分享使用心得。更棒的是,搭配蝦皮分潤計畫,愛用品不僅自己用得安心,分享給朋友還能賺回饋。立即使用推薦碼 X5Q344E,輕鬆上手,隨時隨地賺取分潤!
Thumbnail
身為一個典型的社畜,上班時間被會議、進度、KPI 塞得滿滿,下班後只想要找一個能夠安靜喘口氣的小角落。對我來說,畫畫就是那個屬於自己的小樹洞。無論是胡亂塗鴉,還是慢慢描繪喜歡的插畫人物,那個專注在筆觸和色彩的過程,就像在幫心靈按摩一樣,讓緊繃的神經慢慢鬆開。
Thumbnail
身為一個典型的社畜,上班時間被會議、進度、KPI 塞得滿滿,下班後只想要找一個能夠安靜喘口氣的小角落。對我來說,畫畫就是那個屬於自己的小樹洞。無論是胡亂塗鴉,還是慢慢描繪喜歡的插畫人物,那個專注在筆觸和色彩的過程,就像在幫心靈按摩一樣,讓緊繃的神經慢慢鬆開。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
Vue.js是一種基於MVVM的前端JavaScript框架,類似的框架有React、Angular等。 架設環境 安裝Visual Studio Code(https://code.visualstudio.com/) 安裝Node.js(https://nodejs.org/en/
Thumbnail
Vue.js是一種基於MVVM的前端JavaScript框架,類似的框架有React、Angular等。 架設環境 安裝Visual Studio Code(https://code.visualstudio.com/) 安裝Node.js(https://nodejs.org/en/
Thumbnail
2023 Vue直播班筆記 - 動態路由Props,接續之前的一般動態路由。分為 "寫死" 及 "彈性" 兩種。
Thumbnail
2023 Vue直播班筆記 - 動態路由Props,接續之前的一般動態路由。分為 "寫死" 及 "彈性" 兩種。
Thumbnail
Vue Router 及 具名視圖,擺脫以往切換依賴 CSS display:none 跟 display:block 互相配合,有時還得搭配 z-index 來調整層級跟 opacity 透明度的麻煩,而 Vue Router 完美的解決了這棘手的問題,且能客製頁面想要呈現的擺飾。
Thumbnail
Vue Router 及 具名視圖,擺脫以往切換依賴 CSS display:none 跟 display:block 互相配合,有時還得搭配 z-index 來調整層級跟 opacity 透明度的麻煩,而 Vue Router 完美的解決了這棘手的問題,且能客製頁面想要呈現的擺飾。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
這系列是我在 2023 六角學院 Vue作品實戰班的筆記,筆記以本人理解的方式記錄。此篇主題為 Slot Props 進階應用 ,其中包含單筆資料、多筆資料。
Thumbnail
在 Vue 專案中使用 Apollo Graphql Client 從 API 獲取資料,由於資料結構較為複雜,筆者便跟著網路教學使用 codegen 工具自動化產生 TypeScript 型別定義。在某個元件中,需要使用 defineProps 來撰寫型別定義,結果⋯⋯
Thumbnail
在 Vue 專案中使用 Apollo Graphql Client 從 API 獲取資料,由於資料結構較為複雜,筆者便跟著網路教學使用 codegen 工具自動化產生 TypeScript 型別定義。在某個元件中,需要使用 defineProps 來撰寫型別定義,結果⋯⋯
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News