---
# System prepended metadata

title: Nuxt 3 學習筆記
tags: [nuxt]

---

:::warning
參考[Nuxt 3 學習筆記](https://ithelp.ithome.com.tw/users/20152617/ironman/5934)
:::

## 目錄
[TOC]

## ▊ CSR、SSR、SSG
### ▎客戶端渲染 (Client-Side Rendering, CSR)
所有的頁面渲染 (render) 都透過瀏覽器端的 Javascript 來完成，也就是所有的邏輯、取資料、路由、template 都在客戶端處理，因為此特性也可以做到只渲染部分 HTML 達到更好的效能，體驗上也會比較順暢。

像 **Vue** 這類 **SPA (Single-Page Application)** 框架，會將前後端進行分離的框架，僅專注在畫面上，所以通常是 CSR。

![](https://hackmd.io/_uploads/B1DQwNTCn.png)
![](https://hackmd.io/_uploads/BJUr2NTAn.png)

當HTML下載完後，FCP 會發生，此時畫面會是空白的，若是等待 Javascript 檔案下載的過程比較久，畫面也會持續是空白的，直到完成下載後執行渲染觸發畫面更新才達到 TTI 。

:::info
:information_source: **FCP (First Contentful Paint，首次內容繪製)**
* 瀏覽器在頁面上呈現第一個 DOM 元素的時間，此元素可能不是從伺服器呈現或載入的第一個元素，但它是用戶可以看到的第一個元素。

:information_source: **TTI (Time-to-interactive，可交互時間)** 
* 頁面載入後，使用者可與瀏覽器互動並能給予回應的時間。
:::
#### ▏優點
##### ▢ 使用者體驗較佳
跳轉頁面時，不再需要由伺服器重新渲染整個頁面，前端框架會幫我們實現部分元素更新，所以使用者體驗較佳。
##### ▢ 實現前後端分離
讓前端能更專注 UI 開發，後端專注 API。
##### ▢ 伺服器負擔較小
因為渲染工作皆在客戶端完成，所以伺服器負擔也較小。

#### ▏缺點
##### ▢ 載入速度慢
因為要等待 Javascript 下載和執行渲染，所以使用者必須等待一段時間才能看到頁面的內容，在低階的的裝置上體驗會比較差。

##### ▢ 不利於搜尋引擎最佳化 (Search Engine Optimization, SEO)
因為CSR的特性，搜尋引擎蒐集與索引到的HTML並不包含客戶端所需要即時請求的資料，所以無法解析這些資料可能含有的關鍵字等索引。
:::success
:writing_hand:  **解決方法**
* 採用**預渲染 (pre-rendering)** 讓網頁請求送達伺服器端後，返回包含資料的HTML：像是**Server-Side Rendering** 和 **Static Site Generationa** 等技術。
:::

---

### ▎伺服器端渲染 (Server-Side Rendering, SSR)
又稱為 **後端渲染**，伺服器收到使用者的請求之後，會在伺服器端生成完整的 HTML，因為生成 HTML 的時候會在伺服器端先取得內部或外部 API 資料，所以相較於 CSR 從瀏覽器端取資料的模式，SSR 可以省去多次的來回往返。


![](https://hackmd.io/_uploads/Bk3u9E6C2.png)
![](https://hackmd.io/_uploads/Bym7JS6Rh.png)
伺服器渲染完後 HTML 傳給使用者，此時已有完整的 HTML，接著下載並執行瀏覽器端互動所需要的 Javascript。

#### ▏優點
##### ▢ TTI 較快
因為需要的 Javascript比較少，所以有較快的TTI。也有更多的 JS 預算可以留給第三方 JS 使用。
##### ▢ 有利於 SEO
因為首次進入網站時，HTML已經在伺服器端渲染完畢，搜尋引擎便能準確抓取資料。

#### ▏缺點
##### ▢ TTFB 較慢
因為在伺服器產生完整的 HTML 很花時間。如果同時有許多人造訪 server 造成負擔很重，或是有一些非常慢的 API，都有可能讓 server 的回應速度非常慢。
##### ▢ 互動性體驗差
因為 SSR 的頁面在每次互動之間都要重新讀取頁面，這在使用體驗上就不如 CSR 的頁面順暢。
:::info
:information_source: **TTFB (Time to First Byte，第一個字節時間)**
* Web 瀏覽器在造訪網站後，接收到伺服器回應數據的時間，也就是當使用者的滑鼠點擊網站的那一刻開始，到接收到一個數據資料之間所等待的時間。
:::

---

### ▎靜態頁面生成 (Static Site Generation, SSG)
是產生靜態頁面的技術，使用 SSG 技術的網站通常在編譯打包時期就會連帶編譯產出靜態網頁，因此 SSG 非常適合做內容不大會變動的靜態網站，這些編譯出來的頁面也能上 CDN 被快取。

不過也會因為網站大小而導致因為打包的時間可能過長，且在頁面內容需要改變，就必須要重新編譯打包，所以沒辦法像 CSR 或 SSR 具有動態內容的彈性。

雖然 SSG 有一些缺點與劣勢，但也有 ISR (Incremental Static Regeneration) 等技術來解決。

#### ▏優點
##### ▢ 減輕伺服器負擔
打包編譯時產生出網頁原始碼 HTML 檔案，即靜態資源檔案，因此能很好的搭 CDN 緩存來減輕伺服器負擔。

##### ▢ 有利於SEO
因為打包編譯時產生出網頁原始碼 HTML 檔案，正是可以讓搜尋引擎爬蟲解析的完整網頁資料。

#### ▏缺點
##### ▢ 彈性差
如果頁面經常變動，就得再一次打包編譯，重新產生出新的一份網頁原始碼 HTML 檔案。

### ▎SSR (後端渲染) + SPA (單頁式應用程式)
若是採用SSR渲染首次進入的HTML，再用CSR做後續動態的取資料更新畫面，不就能解決SPA常見的白畫面或SEO優化問題嗎？
=> **Nuxt 框架** 或 Next.js

## ▊ Nuxt 3 介紹

### ▎Isomorphic JavaScript 與 Universal JavaScript
Nuxt 3 是一個 **Isomorphic JavaScript** 框架

#### ▏Isomorphic (同構) JavaScript
同樣的 JavaScript 程式碼可以在客戶端及伺服器端運行，也就是說同一份 Code 除了能在前端瀏覽器也能在後端執行。
#### ▏Universal JavaScript
同樣的 JavaScript 程式碼可以在不同的環境執行，也就是不僅可以在前後端執行，還可以在本機設備和嵌入式架構上運行。

### ▎Nitro

Nuxt 3 由一個全新的伺服器引擎 Nitro 提供支持，它具有以下幾個特點：
* 跨平台支持，支持 Node.js 與瀏覽器等
* Serverless 支持
* API 路由，使用 `unjs/h3`
* 自動程式碼拆分 (code-splitting) 與異步加載 chunk (async-loaded chunks)
* 混合渲染模式，供靜態 (static)與無伺服器 (serverless) 網站
* 開發伺服器上的 HMR (hot module reloading)

### ▎Nuxt 3 渲染模式

#### ▏Client-side Only Rendering
也就是客戶端渲染 CSR
#### ▏Universal Rendering
**是 Nuxt 3 預設的渲染模式**，等於 SSR + SPA。
#### ▏Hybrid Rendering
可以為每個路由設置不同的渲染與緩存規則，讓部分頁面是 CSR，另一部份是 SSR。
#### ▏Edge-Side Rendering
Nitro 為 Nuxt 3 提供支持的全新伺服器端渲染引擎，能為 Node.js、Deno、Worker 等提供跨平台的支持，讓 Nuxt 可以在 CDN Edge Workers 進行渲染。

能有效分擔在伺服器端渲染時的資源負荷，將其提升到另一個層次，從而減少網路延遲及成本。

### ▎Nuxt 3 的建構工具
* Vite (預設) / webpack
* Rollup
* PostCSS
* esbuild

Nuxt 3 已經配置好一堆設定，要調整配置，可以在 `nuxt.config` 中進行調整。

## ▊ 使用 nuxi 建立第一個專案
### ▎ 開始建立
#### ▏使用 nuxi 建立 Nuxt 3 專案
1. 在終端機輸入：`npx nuxi init nuxt-app`

    完成後可以發現目前目錄下多了一個名為 `nuxt-app` 的資料夾，這個資料夾也就是 Nuxt 3 專案的根目錄。
    ![](https://hackmd.io/_uploads/rkK--UTCn.png =120x)

2. 進入專案目錄`nuxt-app`：`cd nuxt-app`
3. 安裝相關套件：`npm install`
4. 啟動 Nuxt：`npm run dev -- -o`


在`app.vue`我們可以看到 `<NuxtWelcome />` ，但沒有看到設定 import 的地方， 因為 Nuxt 可以自動導入元件。
![](https://hackmd.io/_uploads/SJ04HLTAh.png =180x)

## ▊ Nuxt CLI 常用指令
在專案目錄下記得使用 `npx` 來執行 `nuxi`

### ▎`nuxi init`
```
npx nuxi init|create [dir]
```
用來初始化一個 Nuxt 專案，等價 `nuxi create` 指令。

* `dir` 可以填字串作為專案與資料夾名稱，也可以填寫完整路徑來建立專案目錄。

### ▎`nuxi dev`
```
npx nuxi dev [--open, -o] [--port, -p]
```
當我們輸入 `npm run dev -- -o` 時，其實是依據 `package.json` 中的 `scripts` 執行 `nuxt dev -o`。

* `-o`：服務啟動後開啟瀏覽器
* `-p`：將預設的 Port:3000 調整為其他數值

### ▎`nuxi cleanup`
```
npx nuxi clean|cleanup
```
等價 `nuxi create` 指令，用來刪除 Nuxt 自動產生的檔案和緩存，包括：
* `.nuxt`
* `.output`
* `node_modules/.vite`
* `node_modules/.cache`
### ▎`nuxi upgrade`
```
npx nuxi upgrade [--force|-f]
```
用來將目前專案的 Nuxt 3 升級至最新的版本
* `-f`：強制更新

## ▊ 環境建置
### ▎TypeScript
1. 安裝插件
    * Vue Language Features (Volar)
    * TypeScript Vue Plugin (Volar)
3. 安裝 Vue 類型檢查套件：`npm install -D vue-tsc`
5. 調整 `nuxt.config.ts` 設定：`typescript.typeCheck: true`
### ▎ESLint
1. 安裝套件：`npm install -D eslint @nuxtjs/eslint-config-typescript eslint-plugin-vue`
2. 配置 ESLint 設定黨
在根目錄建立 `.eslintrc.js`：
    ```javascript
    module.exports = {
      env: {
        browser: true,
        es2021: true
      },
      extends: ['@nuxtjs/eslint-config-typescript', 'plugin:vue/vue3-recommended'],
      parserOptions: {
        ecmaVersion: 13,
        sourceType: 'module'
      },
      plugins: [],
      rules: {},
      overrides: [
        {
          files: [
            '**/pages/**/*.{js,ts,vue}',
            '**/layouts/**/*.{js,ts,vue}',
            '**/app.{js,ts,vue}',
            '**/error.{js,ts,vue}'
          ],
          rules: {
            'vue/multi-word-component-names': 'off'
          }
        }
      ]
    }
    ```
### ▎Prettier
1. 安裝Prettier 套件：`npm install -D prettier eslint-config-prettier eslint-plugin-prettier`
2. 配置 Prettier 設定檔案
在根目錄建立 `.prettierrc.js`：
    ```javascript
    module.exports = {
      printWidth: 100,          // 每行文字數量達 100 字元就換到新的一行
      semi: false,              // 每個語句的結尾不需要分號
      singleQuote: true,        // 字串使用單引號，而不是雙引號
      trailingComma: 'none'     // 如 Object、Array 內的元素不需要尾隨逗號
    }
    ```

4. 配置 ESLint 設定檔案
    ```
    module.exports = {
      env: {
        browser: true,
        es2021: true
      },
      extends: ['@nuxtjs/eslint-config-typescript', 'plugin:vue/vue3-recommended', 'prettier'],
      parserOptions: {
        ecmaVersion: 13,
        sourceType: 'module'
      },
      plugins: ['prettier'],
      rules: {
        'prettier/prettier': 'error'
      },
      overrides: [
        {
          files: [
            '**/pages/**/*.{js,ts,vue}',
            '**/layouts/**/*.{js,ts,vue}',
            '**/app.{js,ts,vue}',
            '**/error.{js,ts,vue}'
          ],
          rules: {
            'vue/multi-word-component-names': 'off'
          }
        }
      ]
    }
    ```

## ▊ Tailwind CSS
走兩種配置的方法：**使用 Nuxt Tailwind 模組** 和 **Tailwind CSS 官方指引步驟**。
### ▎使用 Nuxt Tailwind 模組
 1. 安裝套件：`npm install -D @nuxtjs/tailwindcss`
 2. 添加模組至 `nuxt.config.ts`
    ```javascript
    modules: ['@nuxtjs/tailwindcss'],
      typescript: {
        typeCheck: true
      }
    ```
    
#### ▏擴充或覆寫`@nuxtjs/tailwindcss`配置
想要修改可以透過建立設定檔來新增或覆寫預設定
##### ▢ `tailwind.css`
若存在路徑檔名一致的 `./assets/css/tailwind.css` 檔案，即可取代預設的 `tailwind.css` 檔案。

```css
/* 預設內容 */
@tailwind base;
@tailwind components;
@tailwind utilities;
```
##### ▢ `tailwind.config.js`
專案目錄下若存在 `tailwind.config.js` 檔案就會以新的配置拓展或覆寫 預設的 `tailwind.config`。

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './components/**/*.{vue,js,ts}',
    './layouts/**/*.vue',
    './pages/**/*.vue',
    './composables/**/*.{js,ts}',
    './plugins/**/*.{js,ts}',
    './app.{js,ts,vue}'
  ],
  theme: {
    extend: {}
  },
  plugins: []
}
```

### ▎Tailwind CSS 官方指引步驟
若使用 `@nuxtjs/tailwindcss` 進行配置可以跳過這段。

1. 安裝相關套件：`npm install -D tailwindcss postcss@latest postcss-custom-properties@latest autoprefixer@latest`
2. 建立 `tailwind.config.js`：`npx tailwindcss init`
4. 調整 `tailwind.config.js`，在 `content` 添加一些路徑：
    ```javascript
    /** @type {import('tailwindcss').Config} */
    module.exports = {
      content: [
        './components/**/*.{vue,js,ts}',
        './layouts/**/*.vue',
        './pages/**/*.vue',
        './composables/**/*.{js,ts}',
        './plugins/**/*.{js,ts}',
        './app.{js,ts,vue}'
      ],
      theme: {
        extend: {}
      },
      plugins: []
    }
    ```
1. 建立 `tailwind.css`：建立目錄 `assets` 與子目錄 `css` 用來放置 Tailwind CSS 的自定義指令， `tailwind.css` 的路徑應會是 `./assets/css/tailwind.css` 檔案內容如下。
    ```css
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    ```
1. 配置全域共用 CSS：修改專案根目錄的 `nuxt.config.ts` 檔案，在 `css` 參數陣列內新增 `tailwind.css` 路徑，讓 Nuxt 可以配置全域共用的 CSS，並添加 `postcss` 選項及我們剛才安裝的套件作為插件，最後 `nuxt.config.ts` 檔案看起來如下。
    ```javascript
    // https://v3.nuxtjs.org/api/configuration/nuxt.config
    export default defineNuxtConfig({
      postcss: {
        plugins: {
          'postcss-import': {},
          'tailwindcss/nesting': {},
          tailwindcss: {},
          autoprefixer: {}
        }
      },
      css: ['@/assets/css/tailwind.css'],
      typescript: {
        typeCheck: true
      }
    })
    ```
    
<br>

建完後專案目錄會是：
```
nuxt-app
├── .nuxt/
├── assets/
│   └── css/
│       └── tailwind.css      // 手動新增的檔案，用於設置 Tailwind CSS 指令並讓全部頁面引用
├── node_modules/
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── app.vue
├── nuxt.config.ts
├── package-lock.json
├── package.json
├── README.md
├── tailwind.config.js       // Tailwind 初始化指令產生的設定檔
└── tsconfig.json
```

### ▎自動排序 Tailwind CSS 的 Class
1. 安裝插件 `npm install -D prettier-plugin-tailwindcss`
2. 配置設定檔
開啟 `.prettierrc.js` 檔案，添加 `'prettier-plugin-tailwindcss'` 至 `plugins` 陣列中：
    ```javascript
    module.exports = {
      plugins: [
        'prettier-plugin-tailwindcss'
      ],
      printWidth: 100,          // 每行文字數量達 100 字元就換到新的一行
      semi: false,              // 每個語句的結尾不需要分號
      singleQuote: true,        // 字串使用單引號，而不是雙引號
      trailingComma: 'none'     // 如 Object、Array 內的元素不需要尾隨逗號
    }
    ```
1. 自動修正效果
設置 `editor.codeActionsOnSave` 中 `source.fixAll.eslint`: `true` 來達到保存後自動修正 prettier 引發的 ESLint 錯誤。
    ```javascript
    "editor.codeActionsOnSave": {
            "source.fixAll": true
        }
    ```
## ▊ Nuxt 3 目錄結構
### ▎Nuxt 3 預設的目錄結構
完整的 Nuxt 3 專案目錄結構：
```
nuxt-app/
├── .nuxt/
├── .output/
├── assets/
├── components/
├── composables/
├── content/
├── layouts/
├── middleware/
├── node_modules/
├── pages/
├── plugins/
├── public/
└── server/
    ├── api/
    ├── routes/
    └── middleware/
├── .gitignore
├── .nuxtignore
├── app.config.ts
├── app.vue
├── nuxt.config.ts
├── package.json
└── tsconfig.json
```
#### ▏`.nuxt` 目錄

開發環境下由 Nuxt 產生出 Vue 的網站，`.nuxt` 目錄是自動產生的，不應該任意的調整裡面檔案。

#### ▏`.output` 目錄

當網站準備部署至正式環境時，每次編譯建構專案時，皆會自動重新產生，不應該任意的調整裡面檔案。

#### ▏`assets` 目錄

靜態資源檔案所放置的位置，目錄內通常包含以下類型的檔案：

-   CSS 樣式檔案 (CSS、SASS 等...)
-   字型
-   圖片

這些靜態資源，最終在專案編譯建構時，由 Vite 或 webpack 進行編譯打包。

#### ▏`components` 目錄

放置 Vue 元件的地方，Nuxt 會**自動載入**這個目錄中的任何元件。

#### ▏`composables` 目錄

組合式函數放置的目錄，簡單來說可以把常用或通用的功能寫成一個共用的函數或 JS 檔案，放置在這個目錄視為組合式函數，Nuxt 也會**自動載入**這些組合式函數，讓需要使用的頁面或元件可以直接做使用。

#### ▏`content` 目錄

透過使用 [Nuxt Content](https://content.nuxtjs.org/)，我們可以在這個目錄下建立 `.md`、`.yml`、`.csv` 和 `.json` 檔案，Nuxt Content 會讀取並解析這些文件並進行渲染，用來建立基於文件的 CMS。

#### ▏`layouts` 目錄

用於放置通用或可能重複使用到的佈局模板，提供程式碼的可重複使用性。

#### ▏`middleware` 目錄

Nuxt 3 提供了路由中間件的概念，用以在導航到下一個頁面之前執行一些程式碼如權限驗證。

#### ▏`node_modules` 目錄

通常有使用 Node.js 的套件管理，例如 NPM，對此目錄應該有一些印象，使用 Nuxt 3 及專案所需要的相依套件都會存放在這個目錄。

#### ▏`pages` 目錄

這個目錄主要是用來配置我們的頁面，你也可以只使用 `app.vue` 來完成你的網站，但如果建立了 `pages` 這個目錄，Nuxt 3 會自動整合 `vue-router`，並會依據目錄及檔案結構規則來`自動產生出對應路由`，也是 Nuxt3 產生路由的方式。

#### ▏`plugins` 目錄

Nuxt 會自動載入這個目錄檔案，作為插件使用，在檔案名稱可以使用後綴 `.server` 或 `.client`，例如， `plugin.server.ts` 或 `plugin.client.ts` 來決定只讓伺服器端或客戶端載入這個插件。

#### ▏`public` 目錄

這個目錄主要用於伺服器根目錄提供的文件，包含必須固定的檔案名稱如 `robots.txt` 或不太會變動的 `favicon.ico`。

#### ▏`server` 目錄

用於建立任何後端的邏輯如後端 API，這個目錄下還包含了 `api`、`server` 和 `middleware` 來區分功能，不具有自動載入，但支援 HMR。

#### ▏`.gitignore` 檔案

在使用 Git 版本控制時，可以設置一些不需要或忽略關注變動的檔案及目錄。

#### ▏`.nuxtignore` 檔案

可以設置讓 Nuxt 編譯建構時，一些不需要或忽略檔案。

#### ▏`app.config.ts` 檔案

提供服務運行時暴露給客戶端使用的設定，因此，請不要在 `app.config.ts` 檔案中添加任何機密資訊。

#### ▏`app.vue` 檔案

Nuxt 3 網站的入口點元件。

#### ▏`nuxt.config.ts` 檔案

用於配置 Nuxt 專案的設定檔。

#### ▏`package.json` 檔案

這個檔案裡面定義了專案資訊、腳本、相依套件及版本號，通常有使用 Node.js 套件管理工具建置的專案都會包含此檔案。

#### ▏`tsconfig.json` 檔案

Nuxt 3 會在 `.nuxt` 目錄下`自動產生`一個 `tsconfig.json` 檔案，其中已經包含了一些解析別名等預設配置；你可以透過專案目錄下的 `tsconfig.json` 來配置擴展或覆蓋 Nuxt 3 預設的 TypeScript 設定檔。
