# [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)

### [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
:::