如何在 Vue 建立動態 title

前言

有時候我們在開發系統時,也會希望網頁的 title 隨著頁面變化,例如首頁、關於我們等,那在 SPA 的 Vue 該怎麼做呢?

從 router 設置

第一種方式相對比較簡單一點,以往我們在設置 router 的導航守衛都會在這邊這樣寫

1
2
3
{
meta: { requiresAuth: true },
},

並搭配導航守衛 (該範例取至 RagnarokShopV3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
const url = `${process.env.VUE_APP_APIPATH}/api/user/check`;
axios.post(url).then((response) => {
if (response.data.success) {
next();
} else {
store.dispatch('updateMessage', {
message: `登入失敗惹Σ( ° △ °|||)︴${response.data.message}`,
status: 'danger',
});
next({
path: '/login',
});
}
});
} else {
next();
}
});

那麼該如何透過 router 來修改 title 呢?只需要在想要變換 title 的 meta 加入 title 即可

1
2
3
4
5
6
7
const router = {
meta:
{
title: '我要變 title',
requiresAuth: true
},
},

最後在針對導航守衛添加以下程式碼即可

1
2
3
4
5
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.title;
}
}

實際的效果你可以在 RagnarokShopV3看到,例如:首頁切換到關於、訂單以及商品等頁都有動態切換 title 的效果。

但其實這邊這個做法有一個問題,當若你在當前畫面下重新整理的話,那麼就會變回預設的 title

因此在這邊若想要使用該方法的話,可以使用 localStorage 來解決該問題,只需要將導航守衛加入 localStorage

1
2
3
4
5
6
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.title;
localStorage.setItem('title', to.meta.title);
}
}

並在實例化 Vue 時,使用生命週期 created 判斷是否有 title,若存在就寫入

1
2
3
4
5
6
7
8
9
10
11
new Vue({
router,
store,
created() {
const title = localStorage.getItem('title') || '';
if (title) {
document.title = title;
}
},
render: (h) => h(App),
}).$mount('#app');

這樣子就完美的解決在當前頁面下重新整理畫面會導致 title 跳回預設的問題。

GitHub Repositories

使用 Vue Meta 套件

Vue Meta

另一個方法則是使用 Vue Meta 套件

只要你安裝這個套件,並在每一個 .vue 檔案下建立一個 metaInfo 並將 title 寫入就可以了

1
2
3
4
5
export default {
metaInfo: {
title: '六腳音樂大舞台',
},
},

當然這套件也可以修改 lang 等等,可以透過官方文件看到

1
2
3
4
5
6
7
8
9
10
export default {
metaInfo: {
title: 'My Example App',
titleTemplate: '%s - Yay!',
htmlAttrs: {
lang: 'en',
amp: true
}
}
}

但是這邊卻有一個問題,若頁面較多時,那麼有幾頁要設置就變成要寫幾次,因此就會非常難以維護,因此這邊一樣可以透過 vue router 的 meta 做法來解決,雖然還是要在特定要修改的頁面加入相關參數,但這邊我們可以將相關 function 寫在 store 當作狀態管理

只是這邊要注意 metaInfo 必須改寫成一個 function

1
2
3
4
5
metaInfo() {
return {
title: this.metaTitle,
};
},

而 Vuex 部分只需要這樣即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export default new Vuex.Store({
state: {
metaTitle: '',
},
mutations: {
SETTITLE(state, payload) {
state.metaTitle = payload;
},
},
actions: {
getMetaTitle(context, title) {
context.commit('SETTITLE', title);
},
},
getters: {
metaTitle(state) {
return state.metaTitle;
},
},
});

實際應用的頁面則只需要這樣改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default {
data() {
return {
};
},
metaInfo() {
return {
title: this.metaTitle || 'HexfootMusic', // 初始化時,若資料還沒進來就顯示預設 title
};
},
methods: {
getTitle() {
this.$store.dispatch('getMetaTitle', this.$route.meta.title);
},
},
computed: {
...mapGetters(['metaTitle']),
},
async created() {
this.getTitle();
},
};

那麼這樣的做法在於未來若我們要修改 title 的時候就只需要專注於 router 的 meta 即可。

實際應用可以看 HexfootMusic

GitHub Repositories

懶人的極致方法

最後這個方式也是最懶人的方式,堪稱接近無腦了,簡單來講就是直接在最上層的 App.vue 寫入判斷即可。

一樣我們在 Router 裡面寫入相關 meta title

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const routes = [
{
path: '/',
name: '',
component: () => import('../views/Layout.vue'),
children: [
{
path: '',
name: 'Index',
component: () => import('../views/Index.vue'),
meta: {
title: '全馬鐵人搜尋器首頁',
},
},
...

最後一步只需要在 App.vue 中寫入一個 watch 監聽 $route 變化

1
2
3
4
5
6
7
8
9
@Component({
name: 'App',
watch: {
$route(to) {
const nowTitle = to.meta.title || 'W3HexSchool 全馬鐵人搜尋器';
document.title = nowTitle;
},
},
})

透過這種方式就算重新整理,也會被立刻修改 title 囉~

實際應用可以參考 W3HexSchool 全馬鐵人搜尋器

GitHub Repositories

參考文獻