:::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 設定檔。