[重構倒數第25天] — Vuex + Composition API 組合技

Mike
I am Mike
Published in
11 min readSep 17, 2021

前言

該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。

今天讓我們來聊聊如果使用 Vuex,要如何搭配 composition api 來幫助我們管理重複的邏輯以及抓取 Vuex 資料。

我們先來看看這次的範例要做什麼

這是一個非常簡單的網頁,有一個會滑入的選單以及滑入的 login 頁面,我們看一下專案結構。

|-- src
|-- App.vue
|-- main.js
|-- assets
| |-- bg.jpg
| |-- close.svg
| |-- logo.png
|-- components
| |-- Header.vue
| |-- Login.vue
| |-- SlidMenu.vue
|-- store
| |-- index.js
|-- view
|-- Home.vue

首先是我們的 Vuex 的 store

import { createStore } from "vuex";

export default createStore({
state: {
loginState: false,
menuState: false,
},
actions: {
handleLoginState({ commit }, bool) {
commit("SET_LOGIN_STATE", bool);
},
handleMenuState({ commit }, bool) {
commit("SET_MENU_STATE", bool);
},
},
mutations: {
SET_LOGIN_STATE(state, bool) {
state.loginState = bool;
},
SET_MENU_STATE(state, bool) {
state.menuState = bool;
},
},
getters: {
loginState: (state) => state.loginState,
menuState: (state) => state.menuState,
},
});

在這邊我們會需要兩個狀態,一個控制Menu、一個控制login的開關,然後設定相對應的 acions 以及mutations,取資料我使用 getters。

接下來看一下Header.vue的內容( CSS的部分我就不秀出來了)

Header.vue

<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const MenuState = computed(() => store.getters.menuState);
const LoginState = computed(() => store.getters.loginState);
const handleMenu = () => {
store.dispatch("handleMenuState", !MenuState.value);
};
const handleLogin = () => {
store.dispatch("handleLoginState", !LoginState.value);
};
return {
headleMenu,
headleLogin,
};
},
};
</script>

<template>
<header>
<nav>
<a @click="handleMenu">Menu</a>
<a @click="handleLogin">Login</a>
</nav>
</header>
</template>

這邊我定義了兩個 function 去對 store dispatch,然後取出 Menu 跟 login 的狀態,去做判斷,然後塞入dispatch中,綁定@click在兩個<a>上,接下來看一下Menu跟Login的部分。

SlidMenu.vue

<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const MenuState = computed(() => store.getters.menuState);
const CloseMenu = () => {
store.dispatch("handleMenuState", false);
};
return { MenuState, CloseMenu };
},
};
</script>
<template>
<div :class="['slid_menu', { active: MenuState }]">
<a class="close" @click="CloseMenu">
<img src="../assets/close.svg" />
</a>
<nav>
<a>ABOTU</a>
<a>ADDRESS</a>
<a>USER</a>
<a>STRENGTH</a>
<!-- 以下省略 -->
</nav>
</div>
</template>

Login.vue

<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const LoginState = computed(() => store.getters.loginState);

return { LoginState };
},
};
</script>
<template>
<div :class="['login', { active: LoginState }]">
<h1>Login Page</h1>
</div>
</template>

我再Menu跟Login身上綁定了一個active的class,如果這個class被add上去了,就會把Menu給滑出來,如果remove掉了,就會收回去,然後Menu加一個function是可以把選單給關閉的。

以上的用法就是一個很典型的Vuex的使用方式,但是你會發現不管是在取得資料,以及操作dispatch的時候,感覺好像可以封裝一下,不用寫這麼多重複的code,這時候composition api 就派上用場啦!!!

我們先新增一個composition-api的資料夾,然後新增一個 useStateHandle.js

useStateHandle.js

import { computed } from "vue";
import { useStore } from "vuex";

export function useStateHandle() {
const store = useStore();

const loginState = computed(() => store.getters.loginState);

const menuState = computed(() => store.getters.menuState);

const stateHandle = (type, bool) => {
store.dispatch(type, bool);
};

return { loginState, menuState, stateHandle };
}

我們可以在這個is裡面使用 computed 跟 getters 的方式取得我們 store 的資料,以及建立一個 function 去執行dispatch ,透過帶入的type參數,來決定執行哪個 actions,最後都return 出去。

我們Vuex完全不用改變任何東西,接下來我們來看一下

Header.vue

<script>
import { useStateHandle } from "@/composition-api/useStateHandle.js";
export default {
setup() {
const { loginState, menuState, stateHandle } = useStateHandle();
return {
loginState,
menuState,
stateHandle,
};
},
};
</script>
<template>
<header>
<nav>
<a @click="stateHandle('handleMenuState', !menuState)">Menu</a>
<a @click="stateHandle('handleLoginState', !loginState)">Login</a
</nav>
</header>
</template>

我們可以看到原本寫的那些 store 的 code 全部不見了,只剩下我從 useStateHandle 裡面取出來的參數,透過我包裝好的 stateHandle,我可以帶入我想執行的 actions 的名稱,然後跟參數,就可以操作執行 Vuex 裡面 state。

Login.vue

<script>
import { useStateHandle } from "@/composition-api/useStateHandle.js";
export default {
setup() {
const { loginState } = useStateHandle();
return { loginState };
},
};
</script>
<template>
<div :class="['login', { active: loginState }]">
<h1>Login Page</h1>
</div>
</template>

SlidMenu.vue

<script>
import { useStateHandle } from "@/composition-api/useStateHandle.js";
export default {
setup() {
const { menuState, stateHandle } = useStateHandle();
return { menuState, stateHandle };
},
};
</script>
<template>
<div :class="['slid_menu', { active: menuState }]">
<a class="close" @click="stateHandle('handleMenuState', false)">
<img src="../assets/close.svg" />
</a>
<nav>
<a>ABOTU</a>
<!-- 以下省略 -->
</nav>
</div>
</template>

這樣我們就可以減少從 Vuex 取的 state 的重複邏輯,以及我們要執行 actions 的 function,可以說是code少了非常多,我們開發網站的時候常常會有很多這樣類型的邏輯是可以封裝的,除了今天介紹的以外,還可以封裝非同步的處理,也可以去對 scroll 軸或是 resize 等事件進行共用邏輯的抽出,可以說是 composition api 提供了非常大的便利性以及管理方式。

此文章同步更新於 第 13 屆 iT 邦幫忙鐵人賽
https://ithelp.ithome.com.tw/articles/10260617

這個專案我有放到 codesandbox 上面,有興趣的朋友就玩玩看吧~

那如果對於Vue3不夠熟的話呢?

Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。

我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/packages/AYR5m7VR3

如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/packages/Q9R4OYoyD

訂閱Mike的頻道享受精彩的教學與分享

Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng

--

--

Mike
I am Mike

如果有一行code無法解決的bug,那就寫兩行!