# 🏅 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 標籤內 ,如下圖 :

如果沒有特別要設定屬性的話,可以省略 `<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://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]() |
-->