iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0
自我挑戰組

自學vue~點亮Roadmap過程系列 第 29

vue3鍊成術第二十九天-組件基礎(1)

  • 分享至 

  • xImage
  •  

組件基礎

組件允許我們將 UI 劃分為獨立的、可重用的部分,並且可以對每個部分進行單獨的思考。在實際應用中,組件常常被組織成層層嵌套的樹狀結構:
https://ithelp.ithome.com.tw/upload/images/20241012/201692105HqLByLMDy.png
這和我們嵌套 HTML 元素的方式相似,Vue 實現了自己的組件模型,使我們可以在每個組件內封裝自定義內容與邏輯。Vue 同樣也能很好地配合原生 Web Component。

定義一個組件

當使用構建步驟時,我們一般會將 Vue 組件定義在一個單獨的 .vue 文件中,這被叫做單文件組件 (簡稱 SFC):

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">You clicked me {{ count }} times.</button>
</template>

當不使用構建步驟時,一個 Vue 組件以一個包含 Vue 特定選項的 JavaScript 對象來定義:

import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return { count }
  },
  template: `
    <button @click="count++">
      You clicked me {{ count }} times.
    </button>`
  // 也可以針對一個 DOM 內聯模板:
  // template: '#my-template-element'
}

這裡的模板是一個內聯的 JavaScript 字符串,Vue 將會在運行時編譯它。你也可以使用 ID 選擇器來指向一個元素 (通常是原生的 template 元素),Vue 將會使用其內容作為模板來源。

上面的例子中定義了一個組件,並在一個 .js 文件裡默認導出了它自己,但你也可以通過具名導出在一個文件中導出多個組件。

使用組件

我們會在接下來的指引中使用 SFC 語法,無論你是否使用構建步驟,組件相關的概念都是相同的。示例一節中展示了兩種場景中的組件使用情況。

要使用一個子組件,我們需要在父組件中導入它。假設我們把計數器組件放在了一個叫做 ButtonCounter.vue 的文件中,這個組件將會以默認導出的形式被暴露給外部。

<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>

<template>
  <h1>Here is a child component!</h1>
  <ButtonCounter />
</template>

通過 script setup,導入的組件都在模板中直接可用。

當然,你也可以全局地註冊一個組件,使得它在當前應用中的任何組件上都可以使用,而不需要額外再導入。關於組件的全局註冊和局部註冊兩種方式的利弊,我們放在了組件註冊這一章節中專門討論。

組件可以被重用任意多次:

<h1>Here is a child component!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />

你會注意到,每當點擊這些按鈕時,每一個組件都維護著自己的狀態,是不同的 count。這是因為每當你使用一個組件,就創建了一個新的實例。

在單文件組件中,推薦為子組件使用 PascalCase 的標籤名,以此來和原生的 HTML 元素作區分。雖然原生 HTML 標籤名是不區分大小寫的,但 Vue 單文件組件是可以在編譯中區分大小寫的。我們也可以使用 /> 來關閉一個標籤。

如果你是直接在 DOM 中書寫模板 (例如原生 template 元素的內容),模板的編譯需要遵從瀏覽器中 HTML 的解析行為。在這種情況下,你應該需要使用 kebab-case 形式並顯式地關閉這些組件的標籤。

<!-- 如果是在 DOM 中書寫該模板 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>

傳遞 props

如果我們正在構建一個博客,我們可能需要一個表示博客文章的組件。我們希望所有的博客文章分享相同的視覺佈局,但有不同的內容。要實現這樣的效果自然必須向組件中傳遞數據,例如每篇文章標題和內容,這就會使用到 props。

Props 是一種特別的 attributes,你可以在組件上聲明註冊。要傳遞給博客文章組件一個標題,我們必須在組件的 props 列表上聲明它。這裡要用到 defineProps 宏:

<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
</script>

<template>
  <h4>{{ title }}</h4>
</template>

defineProps 是一個僅 script setup 中可用的編譯宏命令,並不需要顯式地導入。聲明的 props 會自動暴露給模板。defineProps 會返回一個對象,其中包含了可以傳遞給組件的所有 props:

const props = defineProps(['title'])
console.log(props.title)

如果你沒有使用 script setup,props 必須以 props 選項的方式聲明,props 對象會作為 setup() 函數的第一個參數被傳入:

export default {
  props: ['title'],
  setup(props) {
    console.log(props.title)
  }
}

一個組件可以有任意數量的 props,默認情況下,所有 prop 都接受任意類型的值。

當一個 prop 被註冊後,可以像這樣以自定義 attribute 的形式傳遞數據給它:

<BlogPost title="My journey with Vue" />
<BlogPost title="Blogging with Vue" />
<BlogPost title="Why Vue is so fun" />

在實際應用中,我們可能在父組件中會有如下的一個博客文章數組:

const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])

這種情況下,我們可以使用 v-for 來渲染它們:

<BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />

留意我們是如何使用 v-bind 語法 (:title="post.title") 來傳遞動態 prop 值的。當事先不知道要渲染的確切內容時,這一點特別有用。


上一篇
vue3鍊成術第二十八天-模板引用
下一篇
vue3鍊成術第三十天-組件基礎(2)+結語
系列文
自學vue~點亮Roadmap過程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言