Layouts 在設計網頁時扮演重要的角色,提供共用的介面以保持多個頁面有一致的外觀與結構。<NuxtLayout> 如果沒有指定 name,預設會使用 layouts/default.vue。使用方式為利用 <slot /> 來挖洞存放頁面的內容,當然也可以根據需求自定義多個不同的佈局。今天就來介紹如何有效的運用 Nuxt 3 的 Layouts。
建立 layout
// layouts/default.vue
<template>
<div>
<h1>default layout</h1>
<slot />
</div>
</template>
使用 <NuxtLayout> 元件
// app.vue
<template>
<NuxtLayout> my contents </NuxtLayout>
</template>

layouts/default.vue 的 <slot /> 就是 app.vue 的 <NuxtLayout> 內包裹元素的顯示位置,此處 "my contents" 可以換成 <NuxtPage />,將畫面設定為 /pages 對應的頁面。app.vue 定義就好。<slot />,以確保在頁面切換、佈局更改時自動應用過渡效果(Transition)。除了 layouts/default.vue 外,須使用其他佈局。
佈局名稱標準為 kebab-case,如果檔名為 creativePlanet.vue,則 layout 名稱會是 creative-planet。
建立 custom layout
// layouts/custom.vue
<template>
<div>
<h1>custom layout</h1>
<slot />
</div>
</template>
需指定 <NuxtLayout> 的 name,若無指定則會是 layouts/default.vue。
name 直接傳字串(name="要使用的佈局檔案名稱")
// app.vue
<template>
<NuxtLayout name="custom"> my contents </NuxtLayout>
</template>
name 用 v-bind 的方式綁定
// app.vue
<template>
<NuxtLayout :name="layout"> my contents </NuxtLayout>
</template>
<script setup lang="ts">
const layout = "custom";
</script>
利用 definePageMeta() 覆蓋預設佈局
// app.vue - 佈局為 layouts/default.vue
<template>
<NuxtLayout> <NuxtPage/> </NuxtLayout>
</template>
// pages/index.vue - 佈局覆蓋為 layouts/custom.vue
<template>
my contents
</template>
<script setup lang="ts">
definePageMeta({
layout: 'custom'
})
</script>
nuxt devtools 查看 / 的 layout 改為 custom ,其他頁面則會是(default)。

⚠ 注意:definePageMeta() 是用來設定頁面的 utils,因此只能在頁面元件(通常位於 pages/ 目錄下,也可以在 nuxt.config.ts 設定其他路徑)的 <script> 或 <script setup> 使用。如果嘗試在不符合上述條件的地方使用,將會失效並出現警告訊息:
definePageMeta() is a compiler-hint helper that is only usable inside the script block of a single file component which is also a page. Its arguments should be compiled away and passing it at runtime has no effect.
這意味著因為不符合上述條件,definePageMeta() 在執行時不會有任何效果。

Layouts 中可以插入自訂的具名 Slots,讓佈局有分區塊需求時,能更加靈活運用。
建立具名的 slot 並設定預設內容。
// layouts/custom.vue
<template>
<div>
<h1>custom layout</h1>
<h2><slot name="header"> default header content. </slot></h2>
<main>
<slot />
</main>
</div>
</template>
使用時指定相對應的 slot 名稱,沒有指定的內容則會放在 <main> 區塊內。
// app.vue
<template>
<NuxtLayout name="custom">
<template #header> Some header template cotent. </template>
The rest of the page.
</NuxtLayout>
</template>

<NuxtLink> 應用將多個頁面共用 Layout,並且可以在佈局內加上 <NuxtLink> 讓使用者能更輕鬆的切換頁面。
目前 layouts/ 及 pages/ 資料結構
nuxt-project
┣ layouts
┃ ┣ custom.vue
┃ ┗ default.vue
┣ pages
┃ ┣ index.vue
┃ ┣ about.vue
┃ ┗ service.vue
增加 <NuxtLink>
// layouts/custom.vue
<template>
<div>
<div>
<div>Welcome to CreativePlanet !</div>
<NuxtLink to="/">Home</NuxtLink>
<NuxtLink to="/about">Who we are</NuxtLink>
<NuxtLink to="/service">What we do</NuxtLink>
</div>
<slot />
</div>
</template>
直接在 app.vue 使用,統一整個網站的佈局
// app.vue
<template>
<NuxtLayout :name="layout">
<NuxtPage />
</NuxtLayout>
</template>
<script setup lang="ts">
const layout = "custom";
</script>

// pages/index.vue
<template>
<div>
<h1>Welcome to CreativePlanet !</h1>
<button @click="EnableCustomLayout"> Update Layout </button>
</div>
</template>
<script setup lang="ts">
const EnableCustomLayout = () => {
setPageLayout('custom')
}
definePageMeta({
layout: false
})
</script>

definePageMeta({ layout: false }) 表示該頁面預設不使用任何佈局,不可用於 app.vue。setPageLayout() 是 Nuxt 3 提供的 Utils,可用來動態變更頁面的佈局,可用於 app.vue。如果還沒建立專案的朋友,也可以先嘗試看看官方文件的線上 layouts 範例。
這兩天介紹了 Pages 和 Layouts,明天會介紹 Middleware 的應用。