# 🏅 Day 13 - SEO Meta Components ## 今日學習目標 - 學習如何使用 Nuxt3 的 SEO 元件來設定靜態與動態的頁面 Meta 資訊 - 理解如何傳入具響應性的資料來動態更新 SEO Meta 資訊 ## 前言 在 Day 11 和 Day12 ,我們學習了四種設定頁面 SEO 的方法,包括全域設定 ( 在 `nuxt.config.ts` 中的`app.head` 預設設定 ) ,以及使用 `useHead()`、`useSeoMeta()`、`useServerSeoMeta()` Composable 來設定單一頁面的 SEO。接下將介紹如何透過 Nuxt3 的內建 SEO 元件,在頁面模板加入靜態和動態變化的 SEO。 ## SEO Meta Components Nuxt3 提供了內建的 SEO 元件,讓我們可以直接在模板中設定 SEO 資訊。以下是這些元件的名稱以及其對應的功能 : - **`<Title>`**:設定 `<title></title>` 標籤,在瀏覽器標籤上顯示頁面標題 。 ``` ex: <Title>網站標題</Title> ``` - **`<Meta>`**:設定 `<meta />`標籤,例如 `description`、`keywords`。 ``` ex: <Meta name="description" content="網頁的描述"/> <Meta name="keywords" content="關鍵詞1, 關鍵詞2"/> ``` - **`<Base>`**:設定 `<base />` 標籤,指定所有連結的基準 URL。 ``` ex: <Base href="https://example.com/"/> ``` - **`<NoScript>`**:設定 `<noscript></noscript>` 標籤,提供給不支援 JavaScript 瀏覽器的提示訊息。 ``` ex; <NoScript>此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</NoScript> ``` - **`<Style>`**:設定`<style></style>`標籤,在 `style` 元素內設定 CSS 。可以有兩種寫法 : ``` ex: // 寫法一. 樣式寫在標籤內 <Style type="text/css"> body { background-color: #f0f0f0; } </Style> // 寫法二. 樣式寫在標籤的 children 屬性 <Style type="text/css" children="body { background-color: #f0f0f0; }"></Style> ``` - **`<Link>`**:設定 `<link />` 標籤,例如引入外部 CSS。 ``` ex: 引入 animate.css <Link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> ``` - **`<Html>`**:設定 `<html></html>` 標籤的屬性,例如 `lang`。在使用上會包在 `<Head>` 和`<Body>`的外層。如果沒有特別要設定屬性的話可以省略這一層 `<Html>` 元件。 ``` ex: <Html lang="en"> <Head></Head> <Body></Body> </Html> ``` - **`<Body>`**:設定 `<body></body>` 標籤的屬性,例如 `class` 或 `id`。在使用上會包在 `<Html>` 的內層。如果沒有特別要設定屬性的話可以省略這一層 `<Body>`元件。 ``` ex: <Html> <Head></Head> <Body class="body" id="body"></Body> </Html> ``` - **`<Head>`**:設定 `<head></head>` 標籤的內容 。在使用上會包在 `<Html>` 的內層,meta 資訊的外層。`<Html>` 如果沒有特別要設定屬性的話可以省略。 ``` ex: <Html> <Head> <Title>網站標題</Title> <Meta name="description" content="網頁的描述。"/> <Meta name="keywords" content="關鍵詞1, 關鍵詞2"/> <!-- 其他元件 --> </Head> </Html> ``` ### 使用方式 使用 SEO 元件有以下四個要點: 1. 為了避免與原生 HTML 標籤名稱重複,SEO 元件的名稱應以大寫字母開頭。例如,使用 `<Meta />` 而不是 `<meta />`。 2. 元件放入屬性名稱與屬性值的用法和原生 HTML 相同。例如原生 HTML 撰寫的 `<meta name="keywords" content="關鍵詞1, 關鍵詞2"/>` 轉換成元件的做法可以寫成 `<Meta name="keywords" content="關鍵詞1, 關鍵詞2"/>` 。 3. 撰寫 SEO 元件時建議依照與原生 HTML 的相同的結構,將頁面的 SEO 資訊包在 `<Head> </Head>` 元件內。 4. SEO 元件在伺服器端和客戶端渲染時所生成的 SEO 資訊是相同的。如果需要針對伺服器端的 SEO 設定資訊,可以使用 `useServerSeoMeta()`。 以下方範例為例,整合在模板會呈現與原生 HTML 相似的結構。 ```html <!-- /pages/index.vue --> <template> <Html lang="en"> <Head> <Title>網站標題</Title> <Meta name="description" content="網頁的描述" /> <Meta name="keywords" content="關鍵詞1, 關鍵詞2" /> <NoScript >此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</NoScript > <Style type="text/css"> body { background-color: #f0f0f0; } </Style> <Link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> </Head> <Body class="body" id="body"> <h1>index 頁面</h1> </Body> </Html> </template> ``` Nuxt 在解析後會將`<Html>` 與 `<Body>` 元件的屬性設定移動至 `<html>` 與 `<body>` 標籤,`<Head>`元件內的 SEO 資訊移動到 `<head></head >`。而 `<Body>` 元件內放的內容不會被移動,會被渲染在 body 標籤內 ,如下圖 : ![day13-usage](https://hackmd.io/_uploads/B10i85FzJl.jpg) 如果沒有特別要設定屬性的話,可以省略 `<Html>` 和 `<Body>` 的嵌套。頁面的內容可以選擇不包在 `<Body>` 元件裡面。如果只有設定 SEO 資訊的需求,可以只保留 `<Head></Head>` 元件。 ```html <!-- /pages/index.vue --> <template> <Head> <Title>網站標題</Title> <Meta name="description" content="網頁的描述" /> <Meta name="keywords" content="關鍵詞1, 關鍵詞2" /> <NoScript >此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</NoScript > <Style type="text/css"> body { background-color: #f0f0f0; } </Style> <Link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> </Head> <h1>index 頁面</h1> </template> ``` ## 傳入具響應性的資料 Nuxt3 的 SEO 元件除了可以接受靜態資料,也可以接受響應性資料。也就是說,可以使用 Vue 的 `ref` 或 `computed` 來設定這些元件的屬性,讓 SEO Meta 資訊能夠根據資料變化即時更新。 ### 使用 ref 設定 SEO Meta 資訊 可以使用 ref 定義具有響應性的資料,將其透過 `{{ }}` 模板語法渲染或是通過 v-bind 綁定至元件的屬性。以下範例使用了 `ref` 設定頁面標題並以 `{{title}}` 渲染,同時將網頁描述綁定至 `<Meta>` 元件的 `content` 屬性。 ```html <!-- /pages/about.vue --> <script setup> const title = ref("網站標題"); const description = ref("網頁的描述"); </script> <template> <Head> <Title>{{ title }}</Title> <Meta name="description" :content="description" /> <!-- 使用 v-bind --> <Meta name="keywords" content="關鍵詞1, 關鍵詞2" /> <NoScript >此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</NoScript > <Style type="text/css"> body { background-color: #f0f0f0; } </Style> <Link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> </Head> <h1>about 頁面</h1> </template> ``` ### 使用 Computed 設定 SEO Meta 資訊 當 SEO Meta 需要透過變數或是邏輯運算來計算具有響應性的值,可以使用 Vue `computed` 處理複雜的邏輯運算,維持模板的整潔與邏輯的維護性。 以下範例使用了 `computed` 計算 socialMediaTitle 作為 og:title 和 twitter:title 共同的 content 屬性值,避免在模板中重複寫多次相同的邏輯。 ```html <script setup> const title = ref("網站標題"); const description = ref("網頁的描述"); const socialMediaTitle = computed(() => `${title.value} | 關於我們 `); </script> <template> <Head> <Title>{{ title }}</Title> <Meta name="description" :content="description" /> <Meta name="keywords" content="關鍵詞1, 關鍵詞2" /> <Meta property="og:title" :content="socialMediaTitle" /> <Meta name="twitter:title" :content="socialMediaTitle" /> <NoScript >此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</NoScript > <Style type="text/css"> body { background-color: #f0f0f0; } </Style> <Link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> </Head> <h1>about 頁面</h1> </template> ``` <br> > 今日學習的[範例 Code - 資料夾: day13-metacomponent-seo-example](https://github.com/hexschool/nuxt-daily-tasks-2024) ## 題目 請 fork 這一份 [模板](https://github.com/jasonlu0525/nuxt3-live-question/tree/day13-metacomponent-seo) 在 `/pages/room/[id].vue` 房型詳細頁面作答,完成以下條件 : - **改寫 SEO Meta 設定**: - 將原本使用的 `useSeoMeta({ })` 方法,改為使用 Nuxt3 的 SEO 元件來渲染頁面的 Meta 標籤(如 `<Title>`、`<Meta>` 等)。 - 使用 `computed` 計算出所需的 SEO Meta 資訊,避免重複邏輯,並將這些資料應用到 SEO 元件中。 ``` /* 請將 useSeoMeta({ }) 改成 Nuxt3 SEO 元件的寫法 重複邏輯的地方可以使用 computed */ useSeoMeta({ title: roomObject.value.name, titleTemplate: (title) => `Freyja | ${title}`, description: () => `${roomObject.value.description}`, ogTitle: () => `Freyja | ${roomObject.value.name}`, ogDescription: () => `${roomObject.value.description}`, ogImage: () => `${roomObject.value.imageUrl}`, ogUrl: () => `https://freyja.travel.com.tw/room/${roomObject.value._id}`, twitterCard: "summary_large_image", twitterTitle: () => `Freyja | ${roomObject.value.name}`, twitterDescription: () => `${roomObject.value.description}`, twitterImage: () => `${roomObject.value.imageUrl}`, }); ``` ## 回報流程 將答案上傳至 GitHub 並複製 GitHub repo 連結貼至底下回報就算完成了喔 ! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答 : https://github.com/jasonlu0525/nuxt3-live-answer/tree/day13-metacomponent-seo --> 回報區 --- | # | Discord | Github / 答案 | | --- | ----- | ----- | |1|kevinhes|[Github](https://github.com/kevinhes/nuxt-daily-mission/tree/day13)| | 2 | Steven |[Github](https://github.com/y7516552/nuxt3-live-question/tree/day13)| | 3 | dargon | [Github](https://github.com/peterlife0617/2024-nuxt-training-homework01/tree/feature/day13) | | 4 | MY | [Github](https://github.com/ahmomoz/nuxt3-live-question/tree/day13-metacomponent-seo-hw) | | 5 | 眼睛 | [Github](https://github.com/Thrizzacode/nuxt3-live-question/tree/day13-metacomponent-seo) | | 6 | LinaChen | [Github](https://github.com/Lina-SHU/nuxt3-live-question)| | 7 | JimLin | [Github](https://github.com/junhoulin/Nuxt3-hw-day-after10/tree/day13) | | 8 | wei_Rio | [Github](https://github.com/wei-1539/nuxtDaily10/tree/Day-13---SEO-Meta-Components) | | 9 | runweiting | [CodePen](https://codepen.io/weiting14/pen/yLmmEov) | | 10 | Rocky | [Github](https://github.com/WuRocky/Nuxt-Day13-SEO-Meta-Components.git) | | 11 | hsin yu | [Github](https://github.com/dogwantfly/nuxt3-daily-task-live-question/tree/day13-metacomponent-seo) | | 12 | Tough life | [Github](https://github.com/hakuei0115/Nuxt_testing) | | 13 | 阿塔 | [Github](https://github.com/QuantumParrot/2024-Hexschool-Nuxt-Camp-Daily-Task/commit/c7aaef6c120c29a1f6a20adf9f28f31b8df54149) | | 14 | tanuki狸 | [Github](https://github.com/tanukili/Nuxt-2024-week01-2/tree/day13-metacomponent-seo) | | 15 | Fabio20 | [Github](https://github.com/fabio7621/nuxt3-live-question-d2/tree/nuxt-live-day13) | | 16 | Ariel | [Github](https://github.com/Ariel0508/nuxtday9/tree/nuxtDay13) | | 17 | lidelin | [Github](https://github.com/Lide/nuxt3-live-question/tree/day13-metacomponent-seo) | | 18 | Johnson | [Github](https://github.com/tttom3669/2024_hex_nuxt_daily/tree/day13-metacomponent-seo) | <!-- 快速複製 | --- | --- | [Github]() | -->