目錄
client-only component
client-only的用途是使組件只在客戶端渲染
另外也可以透過slot的方式先佔個位置,直到client-only渲染完成
<client-only placeholder='Loading...'>
<!-- 只在客戶端渲染 -->
<comments />
<!-- 在伺服器端渲染,直到client only被掛載好前,會出現這個 -->
<template #placeholder>
<comments-placeholder />
</template>
</client-only>
store
在store資料夾
新增index.js檔可以啟動store
,index.js
會被視為是root module
Nuxt會幫你import Vuex,並把store option加到Vue實例
Nuxt的store的state必須是function
// store/index.js
export const state = () => ({// rubyDiamond8
counter: 0,
})
export const getters = {
getCounter(state){
return state.counter;
},
}
export const mutations = {
increment(state){
state.counter++;
},
}
export const actions = {
// some code here
}
也可以自己新增module
。在store資料夾裡的.js
檔會被轉換為具名的模組
(namespaced module)
// store/toDos.js
export const state = () => ({
list: [];
})
export const mutations = {
add(state, text) {
state.list.push({
text,
done: false
});
},
remove(state, { todo }) {
state.list.splice(state.list.indexOf(todo), 1);
},
toggle(state, todo) {
todo.done = !todo.done;
}
}
<template>
<div class="container">
<ul>
<li class="mb-4"><input placeholder="type toDo here" class="p-2" @keyup.enter="addTodo"></li>
<li v-for="(todo, index) in toDoList" :key="`${todo.text}-${index}`" @click="toggle(todo)">
<input :checked="todo.done" type="checkbox">
<span :class="{ 'line-through': todo.done }">{{ todo.text }}</span>
</li>
</ul>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
toDoList () {
return this.$store.state.toDos.list;
},
},
methods: {
addTodo (e) {
this.$store.commit('toDos/add', e.target.value);
e.target.value = '';
},
...mapMutations({
toggle: 'toDos/toggle'
}),
}
}
</script>
Nuxt生命週期
Nuxt的生命週期
描述在build後發生的事,至於發生甚麼則根據有沒有開SSR模式而改變
,更進一步說會根據SSR或靜態生成改變
在伺服器端運行
的函式(ex:nuxtServerInit、middleware、validate、asyncData)無法
透過this
操作Vue實例
server side
(2022/10/5更新)
如果是SSR的話,每次發出initial request就會執行以下的步驟
如果是靜態生成,這些步驟只有在build時每個頁面執行一次
Nuxt生命週期
(Vue本身的生命週期)
伺服器啟動
nuxt start
開始生成
nuxt generate
serverMiddleware
Nuxt內部有connect實例,可以用它註冊
一些額外的api route
,且不須
要自己準備伺服器
先準備好js檔當作後端API
// /server-middleware/logger.js const express = require('express'); const app = express(); app.get('/', function(req, res){ res.send('Hello Nuxt'); }) module.exports = app;
在nuxt.config.js設定serverMiddleware
serverMiddleware是個陣列,裡面可以放字串、物件、function
// nuxt.config.js export default { serverMiddleware: [ // // path是api路徑,handler是API檔案 {path:'/test_api', handler:'~/server-middleware/logger'}, ] }
使用axios打API
<script lang="ts"> import Vue from 'vue'; import axios from 'axios' export default Vue.extend({ name:'Index', methods:{ async axiosGet(){ const res = await axios.get('/test_api'); console.log(res); } } }) </script>
結果如下
server side Nuxt plugin
依據在nuxt.config.js內的順序跑nuxtServerInit
其功能是將
資料從伺服器傳到客戶端
,在universal模式下每次發request都會呼叫它,頁面載入後也會自動被呼叫用於Vuex,但
只有寫在store/index.js才有用
第1個引數為Vuex context,第2個為Nuxt context
// store/index.js export const state = () => ({ user: {}, }) export const mutations = { setUser(state, data){ state.user = JSON.parse(JSON.stringify(data)); }, } export const actions = { async nuxtServerInit({commit}) { // 把打API的code(非同步)寫在nuxtServerInit時,一定要放在actions // 因為mutations必須是同步的 const { name, id } = await fetch('https://api.github.com/users/tempura327').then(response => response.json()); commit('setUser', { id, profile:`I'm ${name}`, }); }, }
middleware
處理route的hook,渲染
頁面、組件前呼叫
- global middleware
影響設定在nuxt.config.js裡的route - layout middleware
影響設在layouts的route群組 - route middleware
影響設在單個頁面的route
- global middleware
validate
用於驗證router的參數,所以常在動態router的場景出現
除了
SSR
的情況下會在頁面初始化
時被呼叫,CSR
的情況下切換router
也會呼叫
這個hook
一定要return true或false
引數是Router和Vuex混合的context
<script lang="ts"> import Vue from 'vue'; export default Vue.extend({ name: 'UserDetail', // _UserDetail.vue是User底下動態router的child validate({params}) { // params是動態router帶的參數(ex:User/3123 or User/2210的數字) // 只要包含數字就踢到error page return !(/^[0-9]+$/.test(params.UserDetail)); }, }); </script>
asyncData
只能放在page
,其回傳值
會被塞到data
async asyncData(){ const {id, name} = await fetch('https://api.github.com/users/tempura327').then(response => response.json()); // data裡面會被塞素githubUser這個屬性 return { githubUser: { id, name }}; }
在Vue實例被建立前就會被呼叫,所以在這個hook不能使用this
,但是可以接收到context,並用context來抓資料,當資料抓好後Nuxt會自行混入data
因為在切換router時asyncData內的promise會被處理好,所以不會需要loading的畫面,但如果需要的話也可以在nuxt.config.js做設定
loading: { // loading bar can be used to indicate a loading state to the user
color: 'blue',
height: '12px'
},
資料沒抓好前asyncData會阻擋router的轉換(route navigation),如果發生error則會導到error page
- (beforeCreate)
(created)
fetch
fetch有新舊之分,但都不回傳資料
新版從2.12開始,本文的fetch是指新的fetch
在舊fetch只能放page,且無法操作Vue實例。而2.12之後的新fetch
可以放在所有component
(ex:/pages、/layouts、components/)由於fetch在component的
Vue實例
在伺服器端上建立後才被呼叫
,故可以用this
操作Vue實例,也因此常用於在打API
,然後
將資料塞到state
,讓多個頁面去state取共用資料的情況fetch hook的行為可以透過一些屬性來設定
- fetchOnServer: boolean,設定在SSR時是否呼叫fetch。如果為false,$fetchState.pending會變true
- fetchKey: string | function,當在SSR時key會被用於將fetch得到的結果和data配對(類似mapState的感覺)
透過$fetchState
能取得
fetch的狀態
,方便開關loading、顯示錯誤提示父組件
<template> <!-- keep-alive-props設置快取住的葉面的最大數量 --> <NuxtChild keep-alive :keep-alive-props="{ max: 10 }"></NuxtChild> </template>
子組件
<script> import Vue from 'vue'; export default Vue.extend({ name: 'Fetch', data() { return { account:'tempura327', } }, async fetch() { const res = await fetch(`https://api.github.com/users/${this.account}`) .then(response => response.json()); this.$store.commit('setUser', res); }, watch: { // 一般來說query string改變時,fetch是不會被呼叫的,但是可以使用watch解決 '$route.query': '$fetch', }, activated(){ // 配合NuxtChild 和keep-alive使用,頁面切換再切回來若超過5秒則重新fetch if (this.$fetchState.timestamp <= Date.now() - 5000) { this.$fetch(); } }, }) </script>
將state序列化
- 渲染HTML
- HTML檔案生成
client side
(2022/10/5更新)
以下的hook會在瀏覽器上執行,所以不論選擇的模式都會執行
- (從伺服器端)得到HTML
- 載入資源
client side Nuxt plugin
依據在nuxt.config.js內的順序跑Vue Hydration
hydration
是指客戶端
的處理程序
當Vue掌控了從伺服器端取得的HTML,它會將在客戶端上生成的VNode物件與其混合,以將HTML轉換為動態的DOM
如此一來變成在客戶端上的資料產生變化時做出反應Hydration refers to the client-side process during which Vue takes over the static HTML sent by the server and turns it into dynamic DOM that can react to client-side data changes.
middleware
處理route的hook,渲染
頁面、組件前呼叫
- global middleware
影響設定在nuxt.config.js裡的route - layout middleware
影響設在layouts的route群組 - route middleware
影響設在單個頁面的route
- global middleware
asyncData(阻塞)
- (beforeCreate)
- (created)
- fetch(不阻塞)
- (beforeMount)
- (mounted)
參考資料
Nuxt - Components
Nuxt - Lifecycle
USING NUXTSERVERINIT IN VUEX TO FETCH COMPONENT DATA
Nuxt - Data Fetching
Nuxt - The validate method
Nuxt - The serverMiddleware property