# [Nuxt] Nuxt 2 ###### tags: `Nuxt` ## [創建](https://v2.nuxt.com/docs/get-started/installation) :::warning Node 版本建議 16.x 或 14.x,不支援 18.x ::: 以下四種方式任選一個 * `yarn create nuxt-app <project-name>` * `npx create-nuxt-app <project-name>` * `npm init nuxt-app <project-name>` * `pnpm create nuxt-app <project-name>` ## 資料夾結構 僅列出部分內容,完整請看[官網](https://v2.nuxt.com/docs/directory-structure/nuxt) | 資料夾 | 內容 | | ----- | ---- | | [assets](https://v2.nuxt.com/docs/directory-structure/assets) | 需要經過打包編譯的檔案,例如:css、image、svg、fonts 等 | | [components](https://v2.nuxt.com/docs/directory-structure/components) | 元件.vue<br>檔名採大駝峰式命名 | | [layouts](https://v2.nuxt.com/docs/directory-structure/layouts) | 頁面共用的版型 | | [middleware](https://v2.nuxt.com/docs/directory-structure/middleware) | 攔截 Router 的變化<br>切換網址時,中間攔截的處理階層,例如:進入頁面前要驗證 cookie、token | | [pages](https://v2.nuxt.com/docs/directory-structure/pages) | 頁面.vue<br>自動產生 router,不需要個別設定<br>檔名(小寫)等同路徑名 | | [plugins](https://v2.nuxt.com/docs/directory-structure/plugins) | 在 Global 下使用的套件 | | [static](https://v2.nuxt.com/docs/directory-structure/static) | 不需要經過打包壓縮的檔案,例如:favicon.ico、pdf 等 | | [store](https://v2.nuxt.com/docs/directory-structure/store) | Vuex | ## [生命週期](https://v2.nuxt.com/docs/concepts/nuxt-lifecycle) ![Nuxt.js Lifecycle Hooks](https://v2.nuxt.com/_nuxt/image/de48ca.svg) ### [asyncData](https://v2.nuxt.com/docs/features/data-fetching#async-data) * 網頁 render 到瀏覽器前在 server 端執行的生命週期,只執行一次 * 要進行非同步處理且資料需做 SEO 時使用 * 會等執行完才跳轉頁面 * 只能在 pages/ 內使用 * 可以直接 `return` 資料到 `<template>` 內 * `return` 的變數名稱若與 `data()` 內的變數名相同,會覆寫 `data()` 內的值 * 無法使用跟瀏覽器有關的 API,例如:document、window * 無法使用 `this` * 若想取得 store 內的資料 ```javascript asyncData(context) { context.app.store.state.xxx } ``` ### [fetch](https://v2.nuxt.com/docs/features/data-fetching) * Nuxt 2.12 版本後才有 * 在 server 端執行 * 若設定 `fetchOnServer: false`,則只會在 client 端執行,沒有 SSR ```javascript async fetch() { this.posts = await this.$http.$get('https://api.nuxtjs.dev/posts') }, fetchOnServer: false, ``` * 可以在任何一個 component 內執行 * 不能直接 `return` 資料到 `<template>` 內 * 可使用 `this` * 可使用 `this.$fetch()` 手動執行 * 狀態 * $fetchState.pending: `Boolean`。執行是否完成 * $fetchState.error: `null` 或 `Error`。執行是否發生錯誤 * $fetchState.timestamp: 最後一次執行的時間。 * 搭配 `keep-alive`、`activated()` 使用 ```htmlmixed <template> <nuxt keep-alive /> </template> ``` ```javascript export default { data() { ... }, activated() { // Call fetch again if last fetch more than 30 sec ago if (this.$fetchState.timestamp <= Date.now() - 30000) { this.$fetch(); } }, async fetch() { ... } } ``` ### 執行順序 黃色為在 server 端執行,底線為在 client 端執行 ==asyncData > beforeCreate > created > fetch== > ++beforeCreate > created > mounted++ :::info 相關文章: * [Understanding how fetch works in Nuxt 2.12](https://nuxt.com/blog/understanding-how-fetch-works-in-nuxt-2-12) * [簡體中文翻譯 by AtomG](https://juejin.cn/post/6844904168151334919 "[译] 理解Nuxt 2.12中的 fetch") ::: ## [nuxt.config](https://v2.nuxt.com/docs/directory-structure/nuxt-config) ```javascript export default { ssr: false // 預設為 true(Server Side rendering)。false(Client Side rendering) } ``` ## Router | | Nuxt 2 | Vue Router | | --------------------- | ----------- | ------------- | | Router 換頁的進入點 | <Nuxt> | <router-view> | | 嵌套 Router 換頁的進入點 | <NuxtChild> | <router-view> | | 換頁的超連結 | <NuxtLink> | <router-link> | * [`<Nuxt>`](https://v2.nuxt.com/docs/directory-structure/pages#dynamic-pages): 只能在 `layout/` 內使用 * [`<NuxtChild>`](https://v2.nuxt.com/docs/features/nuxt-components#the-nuxtchild-component) * [`<NuxtLink>`](https://v2.nuxt.com/docs/features/nuxt-components#the-nuxtlink-component) ### [動態 router](https://v2.nuxt.com/docs/directory-structure/pages#dynamic-pages) 以 `_` 開頭為檔名,後面接著參數名稱 範例:檔名為 `_id.vue` ```javascript export default { async asyncData(context) { console.log(context.params.id); }, }; ``` ## [Plugins](https://v2.nuxt.com/docs/directory-structure/plugins) ### [1. 自己撰寫的 plugin](https://v2.nuxt.com/docs/directory-structure/plugins#inject-in-root--context) 在 `plugins/` 內新增 js 檔案,並修改 `nuxt.config.js` 的 `plugins` ```javascript // plugins/xxx.js export default ({ app }, inject) => { inject('TestPlugin', { log: (val) => console.log(val) }); }; ``` ```javascript // nuxt.config.js export default { plugins: ['~/plugins/xxx.js'], }; ``` ### [2. Vue plugins](https://v2.nuxt.com/docs/directory-structure/plugins#vue-plugins) :::danger 要先確認是否支援 SSR! ::: 安裝套件後,在 `plugins/` 內新增 js 檔案,並修改 `nuxt.config.js` 的 `plugins` 若發生找不到套件 CSS:將 CSS 檔案放到 `assets/`,`nuxt.config.js` 的 `css` 加上 `['~/assets/xxx.css']` ```javascript // plugins/xxx.js // 將套件原本要寫在 main.js 的內容放到這裡 import Vue from 'vue'; import xxx from 'v-xxx'; Vue.use(xxx); ``` ```javascript // nuxt.config.js export default { plugins: ['~/plugins/xxx.js'], }; ``` ### 3. Nuxt plugins 安裝套件後,修改 `nuxt.config.js` 的 `modules` ```javascript // nuxt.config.js export default { modules: ['@nuxtjs/axios'], }; ``` 若想額外設定共用的事件,在 `plugins/` 新增同名的 js,並修改 `nuxt.config.js` 的 `plugins` ```javascript // plugins/axios.js export default function ({ $axios, redirect }) { $axios.onError(error => { if (error.response.status === 500) { redirect('/sorry') } }) } ``` ```javascript // nuxt.config.js export default { modules: ['@nuxtjs/axios'], plugins: ['~/plugins/axios.js'], }; ``` ### 調用方式 ```javascript export default { // server 端 - Nuxt > 2.12 asyncData(context) { context.$TestPlugin.log('asyncData - $TestPlugin'); }, // server 端 - Nuxt <= 2.12 asyncData({ app }) { app.$TestPlugin.log('asyncData - $TestPlugin'); }, // client 端 mounted() { this.$TestPlugin.log('mounted - $TestPlugin'); }, } ``` ## [Loading](https://v2.nuxt.com/docs/features/loading/) 在換頁時顯示 Progress Bar ### 客製化 修改 `nuxt.config.js` 內的 `loading` ```javascript export default { // 目前的值為預設值 loading: { color: 'black', // Progress Bar 的顏色 failedColor: 'red', // 錯誤時 Progress Bar 的顏色 height: '2px', // Progress Bar 的高度 throttle: '200', // 顯示前的緩衝時間。毫秒 duration: '5000', // 顯示的最長時間。毫秒 continuous: false, // 超過 duration 時間時是否繼續顯示 Progress Bar css: true, // 是否使用 Progress Bar 預設樣式 rtl: false, // 方向。false(左到右)。 }, }; ``` ### 使用自訂的 component component 的 `methods` 裡必須要有 `start()` 和 `finish()`,可再設定 `fail(error)` 和 `increase(num)`。製作完成後修改 `nuxt.config.js` 內的 `loading` ```javascript export default { loading: '~/components/xxx.vue', }; ``` ### 禁用 #### 整個專案:在 `nuxt.config.js` 加上 `loading: false` ```javascript export default { loading: false, }; ``` #### 特定頁面:在 `<script>` 加上 `loading: false` ```htmlmixed <script> export default { loading: false, }; </script> ``` ## [Error Page](https://v2.nuxt.com/docs/directory-structure/layouts#error-page) * 在 `layout/` 內新增 `error.vue` * 不能在 `<template>` 內使用 `<Nuxt>` * 有 `props: ['error']`,可透過 `error.statusCode` 判斷錯誤碼 ```htmlmixed <!-- 官網範例 --> <template> <div class="container"> <h1 v-if="error.statusCode === 404">Page not found</h1> <h1 v-else>An error occurred</h1> <NuxtLink to="/">Home page</NuxtLink> </div> </template> <script> export default { props: ['error'], layout: 'blog' // you can set a custom layout for the error page } </script> ``` ## Vuex :::danger 不要在 Vuex 和 data() 同時存在相同的資料,可能會導致資料不同步。 可採用 computed() 取出存進 Vuex 的資料。 ::: ## 其他功能 ### 跨域 CORS API 1. 安裝 [@nuxtjs/proxy](https://github.com/nuxt-community/proxy-module):`npm install @nuxtjs/proxy` 2. 修改 `nuxt.config.js` 的 `modules` ```javascript export default { modules: ['@nuxtjs/proxy'], proxy: { '/VsWeb/api/*': 'https://www.vscinemas.com.tw', }, } ``` 3. 修改 request url ```javascript // 原本的 request,會有 CORS // const resp = await this.$axios.get('https://www.vscinemas.com.tw/VsWeb/api/GetLstDicCinema'); // 使用 @nuxtjs/proxy const resp = await this.$axios.get('/VsWeb/api/GetLstDicCinema'); ``` ### [local 端開發時使用 HTTPS](https://v2.nuxt.com/docs/configuration-glossary/configuration-server#example-using-https-configuration) 記得將 `server.key` 和 `server.crt` 改成自己的 https 憑證的檔名和存放的路徑 ```javascript // nuxt.config.js import path from 'path'; import fs from 'fs'; export default { server: { https: { key: fs.readFileSync(path.resolve(__dirname, 'server.key')), cert: fs.readFileSync(path.resolve(__dirname, 'server.crt')), }, }, }; ``` ### [多國語系 i18n](/mNJ3kQlkRGS03alv0eOk2Q) --- :::info 建立日期:2023-08-08 更新日期:2023-08-11 :::