EP22 - 屬性

更新於 2024/09/30閱讀時間約 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
2會員
71內容數
分享生活趣事~
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
卡關的人生 的其他內容
接下來要深入探討組件註冊的內容!Vue 組件需要註冊,讓 Vue 知道在模板中遇到組件時在哪裡找到實現。註冊方式有全局和局部兩種。全局註冊適用於通用、頻繁使用的組件,例如按鈕、模態框等;而局部註冊適用於特定頁面或功能,能明確依賴關係,提升維護性。回顧之前的範例,我們都是使用局部註冊呢!
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
這個範例包含六個基礎範例,接下來的實用範例將會更有趣。第四個範例關於條件與迴圈,實作起來也不難。在介紹這個範例之前,注意到如果組件的 <style> 標籤中缺少 scoped 屬性,樣式會全局生效,影響到其他組件的按鈕樣式。因此,請確保每個組件的樣式都加上 scoped。
範例中,message 用於顯示動態標題,isRed 控制段落顏色的切換,color 用於設定文本顏色。透過點擊事件,使用者可以切換 isRed 和 color 的值。具體來說,當滑鼠懸停在 <span> 上時,會顯示 "Hello World!" 的標題;點擊段落則會改變其顏色。
這次可以將範例內容轉移到components資料夾中,並在App.vue中引用它們。在App.vue中使用import引入其他組件。新範例中定義了兩個函數:一個用於反轉字串,另一個用於彈出警告框。透過按鈕點擊可觸發這些功能。這些範例簡單易懂,實作會比閱讀文件更有效率!
接下來要深入探討組件註冊的內容!Vue 組件需要註冊,讓 Vue 知道在模板中遇到組件時在哪裡找到實現。註冊方式有全局和局部兩種。全局註冊適用於通用、頻繁使用的組件,例如按鈕、模態框等;而局部註冊適用於特定頁面或功能,能明確依賴關係,提升維護性。回顧之前的範例,我們都是使用局部註冊呢!
Ex6SimpleComponent.vue 使用 ref 來創建響應式變數 groceryList,並引入 TodoItem 子組件。模板部分使用 <ol> 標籤來創建有序列表,並透過 v-for 指令遍歷 groceryList 陣列,每個項目對應一個 TodoItem 組件。
在這個範例中,我們展示了如何使用 Vue 的 v-model 來實現各種表單元件的雙向綁定。範例包括文字輸入框、單選框、多選框、單選按鈕、下拉選單和多選下拉選單。使用 ref 創建響應式變數,並在 template 中使用 v-model 進行雙向綁定。這使得表單元件能夠即時反映和更新數據。
這個範例包含六個基礎範例,接下來的實用範例將會更有趣。第四個範例關於條件與迴圈,實作起來也不難。在介紹這個範例之前,注意到如果組件的 <style> 標籤中缺少 scoped 屬性,樣式會全局生效,影響到其他組件的按鈕樣式。因此,請確保每個組件的樣式都加上 scoped。
範例中,message 用於顯示動態標題,isRed 控制段落顏色的切換,color 用於設定文本顏色。透過點擊事件,使用者可以切換 isRed 和 color 的值。具體來說,當滑鼠懸停在 <span> 上時,會顯示 "Hello World!" 的標題;點擊段落則會改變其顏色。
這次可以將範例內容轉移到components資料夾中,並在App.vue中引用它們。在App.vue中使用import引入其他組件。新範例中定義了兩個函數:一個用於反轉字串,另一個用於彈出警告框。透過按鈕點擊可觸發這些功能。這些範例簡單易懂,實作會比閱讀文件更有效率!
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
一切從"物件(Object)"開始 1.建立新物件 2.編輯物件內容 3.在物件中新增區塊
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
如何在 Vite 專案中安裝和設置 TypeScript 及路徑別名的步驟,包括安裝必要的依賴、配置 vite.config.js、tsconfig.json 的設置,及如何創建類型聲明文件來正確識別 .vue 文件。
Thumbnail
各位使用 Vue.js 開發的小夥伴們,你們都怎麼實作父子層組件資料的雙向綁定呢?如果你還在寫 prop + emit 的話,不妨進來看看吧。
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節是一個初級的 TypeScript 教學,主要介紹了 TypeScript 中物件導向程式設計的各種核心概念,包括類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda 表達式、泛型和反射等。每個概念都通過詳細的解釋和實例代碼來進行深入的介紹。
Thumbnail
本章節旨在介紹 TypeScript 的基本資料型別,包括內建型別、型別轉換、自訂型別、元組、集合、陣列、和字典型別。透過理解和使用這些型別,可以提高代碼的可讀性和可維護性。
Thumbnail
一切從"物件(Object)"開始 1.建立新物件 2.編輯物件內容 3.在物件中新增區塊
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
平常我們在 html 上常看到的例如 v-for、v-model 等等... 也是VUE已經幫我們定義好的指令,而這次我們可以依這自己的需求來建立。 此功能屬於較進階的功能,因此實戰中會比較少見,市面上還是有不少完善的套件能達到同樣效果,建議可以先往這方面察找
Thumbnail
我們在實作中,難免會遇到在不同組件中,卻有需求相同的資料格式,因此 mixins 可以達到我們的需求,除了 data 以外也包含了 methods 可以共用,舉例來說,學生資料可能會在,班級跟社團內被使用,當我們要撰寫元件時,就可以省略多餘的 data 定義。