Nuxt.js Lifecycle 生命週期

版本:nuxt 2.15.8

Nuxt 最大的特點就是 Server Side Render,因此他有獨立的生命週期,來看一下官方提供的圖片:

廣告

nuxtServerInit

只在 Nuxt 環境初始化時觸發,當我們想將 server 端資料提前傳給 client 端,可以使用此方法,要注意只能寫在 VueX store/index.js actions

// store/index.js
export const state = () => ({
userInfo: {}
});

export const mutations = {
setUserInfo(state, value) {
state.userInfo = value;
}
};

export const actions = {
nuxtServerInit({ commit }, { req }) {
// req.session.user = { name: 'claire' }
commit('setUserInfo', req.session.user);
}
};

這樣就可以在 Nuxt 初始化時,觸發 nuxtServerInit 方法,將值傳入 state,我們可以從瀏覽器 Vue 開發者工具看到內容:

如果想將資料傳給其他 VueX modules,可以這樣做:

首先新增一支檔案 store/greeting.js

// store/greeting.js
export const state = () => ({
message: ''
});

export const mutations = {
setMessage(state, value) {
state.message = value;
}
};

接著在 store/index.js 定義 nuxtServerInit

// store/index.js
export const actions = {
nuxtServerInit({ commit }, { req }) {
commit('greeting/setMessage', 'Hello World!');
}
};

這樣就可以觸發 store/greeting.js setMessage 方法,見下圖開發者工具

Route Middleware

中間組件,在頁面渲染前執行,有三種定義方式,執行順序為:Global → Layout → Page

接下來分別說明該如何定義

Global Middleware

在 middleware 資料夾內建立檔案,這裡命名為 global.js

// middleware/global.js
export default ({ from, route, redirect, store, error }) => {
console.log('global middleware');

if (!store.isLogin) {
redirect('/login');
}
};

接著在 nuxt.config.js 配置

// nuxt.config.js
export default {
router: {
middleware: [ 'global' ]
}
}

Layout Middleware

建立 middleware 檔案,這裡命名為 middleware/layout.js,然後配置到任一 layouts 檔案,範例使用 layouts/default.vue

// layouts/default.vue
export default {
name: 'Default',
middleware: 'layout'
};

或是匿名配置也可以:

// layouts/default.vue
export default {
name: 'Default',
middleware({ from, route, redirect, store, error }) {
console.log('layout middleware');
}
};

Page Middleware

概念同 layouts middleware,配置於任一 pages 檔案,範例使用 pages/about.vue,這裡使用匿名配置來說明

// pages/about.vue
export default {
name: 'About',
middleware({ from, route, redirect, store, error }) {
console.log('page middleware');
}
};

接著我們從開發者工具查看 console 結果依序為下圖,因此我們可以透過 layout 跟 page middleware 來覆寫 global middleware

validate

於 pages 檔案配置此方法,用來驗證動態路由參數有效性,範例使用 pages/about/_userId.vue

// pages/about/_userId.vue
export default {
name: 'User',
validate({ params, query }) {
return true; // 驗證通過
return false; // 驗證無效,會自動轉導 error page
}
}

驗證通過必須 return true,否則會自動轉跳 404 error page

asyncData

於 server 端處理非同步的生命週期,在此傳入的內容可以被搜尋引擎爬蟲取得,是提升 SEO 效能的重點生命週期。

只會在頁面載入時調用,由於生命週期在 Vue 之前,因此無法取得 this ,且 asyncData 僅限 pages 底下頁面使用,方法內會自動帶入 context 參數,我們可以安裝 @nuxtjs/axios 套件,axios 會被注入進 context 內,我們可以物件解構方式使用( { $axios, params } = context )(範例使用 pages/about.vue)

// pages/about.vue
export default {
name: 'About',
async asyncData({ $axios, params }) {
const id = params.id;
const { data } = await $axios.$get(`/api/user/${id}`);
return {
userName: data
};
}
}

透過 return value,資料被賦予進 Vue 實體,我們透過 this.userName 即可成功取值

// pages/about.vue
<template>
<div>
<h1>{{ userName }}</h1>
</div>
</template>

<script>
export default {
name: 'About',
async asyncData({ $axios, params }) {
const id = params.id;
const { data } = await $axios.$get(`/api/user/${id}`);
return {
userName: data
};
}
}
</script>

data 如果有相同變數名稱,會在 asyncData 生命週期被複寫,所以除非需要再次修改變數,否則請避免重複命名變數

asyncData 是在 server 端、路由更新前即調用,由於是在瀏覽器渲染前的生命週期,因此無法使用 loading placeholder,也不能使用瀏覽器相關 API

fetch

Nuxt v2.12 新增功能,功能類似 asyncData ,在畫面渲染前,同時於 server 端跟 client 端的生命週期,可以使用於任一 .vue 頁面,由於是在 Vue created 之後,因此可以取得 this,初次載入頁面時,fetch 會在 server 端執行,如果是透過 <nuxt-link> 進行路由切換,fetch 在 client 端執行,因此可以在此生命週期加入 loading 效果

<nuxt-link> 為 Nuxt 的路由切換元件,相當於 Vue.js 的 <router-link> ,因此我們只能使用內部連結,外部連結必須使用 <a> 標籤,透過 <nuxt-link> 切換路由,會被視為 SPA 頁面跳轉

以下說明使用方式

<template>
<ul>
<li v-for="(post, key) in posts" :key="key">
{{ post }}
</li>
</ul>
</template>

<script>
export default {
data() {
return {
posts: []
}
},
async fetch() {
const { data } = await this.$axios.$get('/api/posts');
this.posts = data;
}
}
</script>

如果要重複觸發 fetch 生命週期,可以使用 this.$fetch 來呼叫:

<template>
<div>
<ul>
<li v-for="(post, key) in posts" :key="key">
{{ post }}
</li>
</ul>
<button @click="$fetch">重新取得貼文</button>
</div>
</template>

如果我們希望 fetch 只在 client 端運行,可以加上 fetchOnServer: false(預設 true)

取得 fetch 狀態

我們可以透過 this.$fetchState 取得 fetch 當前執行狀態,有以下參數:

pending:Boolean / 是否執行完成,可以在此加入 loading 效果(client 端)

error:null or Error 物件 / 判斷是否發生錯誤

timestamp:整數 / 最後一次執行時間(搭配 activated 使用)

範例:

<template>
<div>
<p v-if="$fetchState.pending">Loading...</p>
<p v-if="$fetchState.error">有東西出錯了</p>
</div>
</template>

<script>
export default {
data() {
return {
posts: []
}
},
activated() {
// 每 30 秒自動呼叫 fetch
if (this.$fetchState.timestamp <= Date.now() - 30000) {
this.$fetch()
}
},
async fetch() {
const { data } = await this.$axios.$get('/api/posts');
this.posts = data;
}
}
</script>

生命週期執行順序

接著我們從瀏覽器開發者工具觀察生命週期執行順序

asyncData() {
console.log('asyncData');
},
fetch() {
console.log('fetch');
},
beforeCreate() {
console.log('beforeCreate');
},
created() {
console.log('created');
},
beforeMount() {
console.log('beforeMount');
}

可以發現,createdbeforeCreate 這兩個 Vue.js 生命週期會同時出現在 server 端跟 client 端,如果要避免方法被重複執行,可以這樣做:

  1. 加上 process.client 判斷

    created(){
    if (process.client){
    // 執行內容
    }
    }
  2. 使用 Nuxt fetch 生命週期

  3. 使用 Vue beforeMount 生命週期


參考文章:

https://stackoverflow.com/questions/60411436/nuxtjs-page-is-created-twice

https://happy9990929.github.io/2021/09/10/vue-nuxt-lifecycle-hooks/

廣告
Nuxt.js VueX Store 搭配 vuex-persistedstate 狀態保存工具 Nuxt.js 套件應用:Swiper 製作輪播動畫

評論

廣告
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×