# 🏅 Day 11 - Global head Settings 與 useHead ## 今日學習目標 - 在 **nuxt.config.ts** 設定全域的 SEO 設定 - 使用 useHead 組合函式設定單一頁面的SEO 設定 ## 全域 head 設定 (Global head Settings) Global head Settings 是用來設定網站所有頁面共用的 `<head>` 資訊,確保所頁面的 SEO 設定一致,並避免在各頁面重複寫相同的 `<head>` 資訊。 ### 撰寫 Global head 的語法 可以在 `nuxt.config.ts` 中的 `app.head` 屬性內以**物件**的方式定義全部頁面的 head 資訊。 ```jsx // nuxt.config.ts export default defineNuxtConfig({ app: { head: { // 在 head 物件內定義 head 資訊 }, }, }); ``` head 資訊是基於 [Unhead](https://unhead.harlanzw.com/)  套件的格式進行撰寫。因為 Nuxt3 整合了 [Unhead](https://unhead.harlanzw.com/) 套件,所以就可以更方便地控制各頁面的 `<head>` 資訊,定義 SEO 相關的 meta tags、外部資源的載入、樣式設定等。接下來將使用範例的方式針對經常使用的屬性進行解說,更多的屬性可以至 [Unhead 官方文件](https://unhead.harlanzw.com/) 閱讀 。 ### 範例 ```jsx // nuxt.config.ts export default defineNuxtConfig({ // ...其他設定 app: { head: { viewport: "width=device-width, initial-scale=1", // 渲染出 <meta name="viewport" content="width=device-width, initial-scale=1"> title: "全域 - Nuxt3 Day11 Global head 練習", // 渲染出 <title>全域 - Nuxt3 Day11 Global head 練習</title> charset: "utf-8", // 渲染出 <meta charset="UTF-8"> meta: [ // 渲染出 <meta name="description" content="透過今天的學習,將會學習到 Nuxt3 Global head 的設定方法"> { name: "description", content: "透過今天的學習,將會學習到 Nuxt3 Global head 的設定方法 ", }, // 渲染出 <meta property="og:title" content="Nuxt3 Day11 Global head 練習"> { property: "og:title", content: "Nuxt3 Day11 Global head 練習" }, { property: "og:url", content: "http://localhost:3000/" }, { property: "og:description", content: "透過今天的學習,將會學習到 Nuxt3 Global head 的設定方法", }, ], script: [ // 從外部載入 lodash 套件 // 渲染出 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script> { src: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js", // 用於調整 script 標籤渲染的位置,值可以是 'head' | 'bodyClose' | 'bodyOpen' tagPosition: 'head' }, ], link: [ // 從外部載入 google 字體 // 渲染 <link rel="preconnect" href="https://fonts.googleapis.com"> { rel: "preconnect", href: "https://fonts.gohogleapis.com" }, // 渲染 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> { rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "", }, // 渲染 <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&display=swap" rel="stylesheet"> { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@100..900&display=swap", }, ], style: [ // 渲染出 <style>h1{ color: red }</style> { children: "h1{ color: red }" }, ], noscript: [ // 渲染出 <noscript>此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。</noscript> { children: "此網頁需要支援 JavaScript 才能正確運行,請先至你的瀏覽器設定中開啟 JavaScript。", }, ], }, }, }); ``` 範例中使用了 `viewport`、`title`、`charset`、`meta`、`script`、`link`、`style` 和 `noscript` 屬性 : - title : 設定全域的標題,在所有頁面元件都套用這個標題。 - viewport : 設定全域網頁的可見區域以及縮放比例。 - charset : 設定 HTML 的字元編碼為 utf-8 。 - meta: 設定 meta 標籤,例如 og:title 。 - script: 載入外部的 JavaScript 資源。其中調整 script 標籤渲染的位置可以使用 [tagPosition](https://unhead.unjs.io/usage/guides/positions#tagposition) 屬性,允許的值有 'head' ( 渲染在 `<head>` 標籤內,預設值) 、 'bodyClose' ( 渲染在 `</body>` 結尾標籤前面 ) 、'bodyOpen’ ( 渲染在 `<body>` 起始標籤前面 ) - link: 載入外部的 CSS 資源。 - style: 定義內嵌樣式,渲染`<style></style>`標籤。 - noscript: 定義在 JavaScript 被禁用時顯示的內容,渲染 `<noscript></noscript>` 。 當設定 `meta`、`script`、`link`、`style` 和 `noscript` 屬性時,這些屬性的值都是以陣列格式呈現,且陣列中的每個元素都是物件格式。 在渲染時,`meta`、`script`、`link` 屬性會渲染相應的 HTML 標籤。陣列內的每個物件代表一個對應的 HTML 標籤,並包含該標籤的屬性和值。例如,`link` 屬性會渲染出 `link` 標籤,陣列中的每個物件都代表一個 `link` 標籤要設定的屬性,每個物件都會被渲染為一個 `link` 標籤,物件內的屬性會對應到 `link` 標籤的屬性。 而 `style` 和 `noscript` 屬性被渲染時,這些屬性名稱會分別生成 `<style>` 和 `<noscript>` 標籤。陣列中的每個元素都會創建一個相應的標籤。 ### 渲染結果 加入全域設定後,在頁面可以透過 “檢視網頁原始碼” 查看 `/pages/index.vue` ( 首頁 ) 頁面渲染後的結果,如下圖。 ![image](https://hackmd.io/_uploads/ry053FtGkx.png) ## 單一頁面 head 設定 (useHead) 上一節在 `nuxt.config.ts` 中設定的 head 資訊屬於全域設定,若希望單獨調整頁面的 head 資訊,可以使用 Nuxt3 提供的 `useHead` Composable, 在單一頁面中設定 head 資訊。 ```html <script setup> useHead({ // 在物件內定義 head 資訊 }); </script> ``` ### 撰寫 useHead 的語法 因為 useHead 也是基於 [Unhead](https://unhead.harlanzw.com/)  套件的格式進行撰寫,所以屬性的用法與撰寫結構和全域 `app.head` 屬性相同。若在頁面元件以 `useHead` 設定了與全域相同的屬性,`useHead` 撰寫的屬性會覆蓋全域的屬性,其他屬性則會沿用全域設定。 ### 範例 例如在 `/pages/index.vue` 使用 `useHead` 加入了首頁的 head 資訊,會覆蓋全域 `title` 和 `meta` 屬性的設定。 ```html <!-- /pages/index.vue --> <script setup> useHead({ title: "首頁 - Nuxt3 Day11 useHead() 練習", meta: [ { property: "og:title", content: "首頁 - Nuxt3 Day11 useHead() 練習", }, { property: "og:image", content: "http://localhost:3000/share.jpg" }, { property: "og:description", content: "首頁 - 透過今天的學習,將會學習到 Nuxt3 useHead() 的使用方法 ", }, { name: "description", content: "首頁 - 透過今天的學習,將會學習到 Nuxt3 useHead() 的使用方法 ", }, ], }); </script> <template> <h1>Page: index</h1> </template> ``` ### 渲染結果 渲染的結果如下圖所示,左側是 `/pages/index.vue` 加入 useHead 之前以全域設定渲染的結果,右側是 `/pages/index.vue` 加入 useHead 之後渲染的結果。 在 `useHead()` 加入的 `title` 和 `meta` 會覆蓋全域 `title` 和 `meta` 的屬性。而其它在 `useHead()` 沒有寫入的屬性則是會繼續沿用全域的設定,例如 noscript、script、link 屬性。 ![image](https://hackmd.io/_uploads/BkNXEaTbJl.png) ### 傳入具響應性的資料 `useHead()` 中所有的屬性都可以使用具有響應性的資料,包括 `ref`、`reactive`、`computed getter`。以 `/pages/index.vue` 的 `useHead()` 為例,可以將 `title` 屬性以 `ref` 定義,在模板中可以對 input 綁定 v-model=”title” 來動態修改瀏覽器頁籤的標題。 ```html <!-- /pages/index.vue --> <script setup> const title = ref("首頁 - Nuxt3 Day11 useHead() 練習"); useHead({ title, meta: [ { property: "og:title", content: "首頁 - Nuxt3 Day11 useHead() 練習", }, { property: "og:image", content: "http://localhost:3000/share.jpg" }, { property: "og:description", content: "首頁 - 透過今天的學習,將會學習到 Nuxt3 useHead() 的使用方法 ", }, { name: "description", content: "首頁 - 透過今天的學習,將會學習到 Nuxt3 useHead() 的使用方法 ", }, ], }); </script> <template> <h1>Page: index</h1> <label for="title"> 修改瀏覽器頁籤的標題 <input type="text" id="title" v-model="title" /> </label> </template> ``` <br> > 今日學習的[範例 Code - 資料夾: day11-nuxt-usehead-examplle](https://github.com/hexschool/nuxt-daily-tasks-2024) ## 題目 請 fork 這一份 [模板](https://github.com/jasonlu0525/nuxt3-live-question/tree/day11-usehead-seo),在 `nuxt.config.ts` 與 `/pages/room/index.vue` ,完成以下條件 : - 在 `nuxt.config.ts` 中定義全域設定,確保以下 head 資訊被應用於所有頁面。 ```html <title>Freyja | 高雄頂級旅館 - 提供奢華住宿體驗</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-Content-Type-Options" content="nosniff"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="author" content="Freyja 旅館"> <meta name="keywords" content="Freyja,Freyja 訂房,高雄旅遊,訂房,住宿,住宿預訂,四人房,雙人房,景觀房"> <meta name="description" content="Freyja 旅館位於高雄,提供頂級的住宿體驗。享受絕美市景與高級設施,讓您的每一刻都充滿奢華與舒適。立即預訂,開啟難忘的住宿之旅!"> <meta name="theme-color" content="#ffffff"> <meta name="robots" content="index, follow"> <link rel="icon" href="/favicon.ico"> <link rel="canonical" href="https://freyja.travel.com.tw"> <meta property="fb:app_id" content="12345678" /> <meta property="og:locale" content="zh-TW" /> <meta property="og:type" content="website" /> <meta property="og:url" content="https://freyja.travel.com.tw" /> <meta property="og:title" content="Freyja | 高雄頂級旅館 - 提供奢華住宿體驗" /> <meta property="og:image" content="https://freyja.travel.com.tw/images/og-image.jpg" /> <meta property="og:description" content="Freyja 旅館位於高雄,提供頂級的住宿體驗。享受絕美市景與高級設施,讓您的每一刻都充滿奢華與舒適。立即預訂,開啟難忘的住宿之旅!" /> ``` - 在 `/pages/room/index.vue` 頁面中,使用 `useHead` 渲染以下 head 資訊,確保覆蓋全域設定中的對應屬性。 ```html <title>Freyja | 房型列表</title> <meta name="description" content="探索 Freyja 頂級房型,從景觀尊榮家庭房到尊爵雙人房,享受絕美市景與舒適空間。立即預訂,享受獨特的住宿體驗!"> <meta property="og:title" content="Freyja | 高雄最頂級的旅館"> <meta property="og:description" content="探索 Freyja 的高雄頂級房型,從景觀尊榮家庭房到尊爵雙人房,享受絕美市景與舒適空間。立即預訂,享受獨特的住宿體驗!"> <meta property="og:image" content="https://raw.githubusercontent.com/hexschool/2022-web-layout-training/main/typescript-hotel/%E6%A1%8C%E6%A9%9F%E7%89%88/room2-1.png"> <meta property="og:url" content="https://freyja.travel.com.tw/room"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="Freyja | 高雄最頂級的旅館"> <meta name="twitter:description" content="探索 Freyja 的高雄頂級房型,從景觀尊榮家庭房到尊爵雙人房,享受絕美市景與舒適空間。立即預訂,享受獨特的住宿體驗!"> <meta name="twitter:image" content="https://raw.githubusercontent.com/hexschool/2022-web-layout-training/main/typescript-hotel/%E6%A1%8C%E6%A9%9F%E7%89%88/room2-1.png"> ``` - 確認 `/pages/room/index.vue` 頁面的 head 設定成功覆蓋了全域 head 中的相同屬性設定。 ## 回報流程 將答案上傳至 GitHub 並複製 GitHub repo 連結貼至底下回報就算完成了喔 ! 解答位置請參考下圖(需打開程式碼的部分觀看) ![](https://i.imgur.com/vftL5i0.png) <!-- 解答: https://github.com/jasonlu0525/nuxt3-live-answer/tree/day11-usehead-seo --> ## 回報區 | Discord | Github / 答案 | | --- | --- | | LinaChen | [Github](https://github.com/Lina-SHU/nuxt3-live-question) | | 眼睛 | [Github](https://github.com/Thrizzacode/nuxt3-live-question/tree/day11-usehead-seo) | | kevinhes | [Github](https://github.com/kevinhes/nuxt-daily-mission/tree/day11) | | hsin yu | [Github](https://github.com/dogwantfly/nuxt3-daily-task-live-question/tree/day11-usehead-seo) | | Jim Lin | [Github](https://github.com/junhoulin/Nuxt3-hw-day-after10/tree/day11) | | Stan | [Github](https://github.com/haoxiang16/nuxt3-live-question/tree/day11-usehead-seo) | | Steven | [Github](https://github.com/y7516552/nuxt3-live-question/tree/day11) | | dragon | [Github](https://github.com/peterlife0617/2024-nuxt-training-homework01/tree/feature/day11) | | MY | [Github](https://github.com/ahmomoz/nuxt3-live-question/tree/day11-usehead-seo-hw) | | Rocky | [Github](https://github.com/WuRocky/Nuxt-Day11-Global-head-Settings-useHead.git) | | Ariel | [Github](https://github.com/Ariel0508/nuxtday6/tree/nuxtday11) | | Tough life | [GitHub](https://github.com/hakuei0115/Nuxt_testing) | | wei_Rio |[Github](https://github.com/wei-1539/nuxtDaily10/tree/Day-11---Global-head-Settings-%26-useHead)| | runweiting | [Github](https://github.com/runweiting/2024-NUXT-Task/tree/day11) | | Fabio20 | [Github](https://github.com/fabio7621/nuxt3-live-question-d2/tree/nuxt-live-day11) | | tanuki狸 | [Github](https://github.com/tanukili/Nuxt-2024-week01-2/tree/day11-usehead-seo) | | 阿塔 | [Github](https://github.com/QuantumParrot/2024-Hexschool-Nuxt-Camp-Daily-Task/commit/d6ebef785da8a65a0d64e177855b4a4be3aa2206) | | Nielsen | [Github](https://github.com/asz8621/nuxt3-daily-task/tree/day11-usehead-seo) | | barry1104 | [Github](https://github.com/barrychen1104/nuxt3-live-question/tree/day11-usehead-seo) | | Johnson | [Github](https://github.com/tttom3669/2024_hex_nuxt_daily/tree/day11-usehead-seo) | | lidelin | [Github](https://github.com/Lide/nuxt3-live-question/tree/day11-usehead-seo) | <!-- 快速複製 | --- | [Github]() | -->