[重構倒數第26天] — 你可能不需要Vuex ( You might not need Vuex )

Mike
I am Mike
Published in
10 min readSep 13, 2021

今天來談談除了Vuex以外,再Vue3裡面另外一種可以管理共用資料的方式。

前言

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

You might not need Vuex

我們在Vue要使用跨多個 component 做資料傳遞溝通的時候除了 Vuex 以外,在 Vue3 的時候我們有多了其他選擇,那就是 Provide / inject

我們可以看到官網上面的這張圖就完整的表達 Provide / inject 的使用概念,你可以在父層使用provide來提供資料,然後再子 component 透過 inject 來使用這些資料,無論你的 component 結構有多深,你的父層都可以為你所有底下的所有的 component 去直接 inject。

我們來看看如何實作,先來看資料夾結構,其他資料夾等我就先不放上來了

|-- src
|-- App.vue // 最上層的component
|-- main.js // 進入點
|-- components
| |-- Content.vue // 一般的UI component
|-- store
| |-- index.js // 不是Vuex,是我們接下來要使用Provide / inject 存放資料的地方
|-- views
|-- Home.vue // 首頁的頁面component

首先我們先來定義 store/index.js

import { reactive, readonly } from "vue";
const state = reactive({
count: 0,
});
export default {
state: readonly(state),
};

在這邊你會看到我透過 vue3 的reactive去定義我的資料,透過 vue 的方法可以確保我的資料被更動的時候可以被更新,當然你可以用ref,就依照你的需求來決定,最後我在 export 的時候用了 readonly 函數包起來,就是要避免外面 inject 的時候不小心改到 state 的資料,至於要怎麼改資料我們等等來說,我們先來看要怎麼在component 去使用。

App.vue

<script>
import { provide } from "vue";
import store from "@/store";
import Home from "@/views/Home.vue";
export default {
components: {
Home,
},
setup() {
provide("mapStore", store);
},
};
</script>

我在 setup 裡面將我們 provide 命名為mapStore ( 借鏡一下Vuex的命名,這樣從Vuex轉換過來也會比較親切 ) ,然後把我剛剛定義好的資料給載入進來,就這麼簡單。

Content.vue

<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state } = mapStore;
return {
...toRefs(state),
};
},
};
</script>

<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button>add</button>
<button>remove</button>
</div>
</template>

這邊透過 inject 的方式把我的 mapStore 給注入進來,接下來我就可以直接取得我定義好的state 資料,然後丟到我的template上,不過這邊要注意我使用了toRefs 去做包我的 state,為什麼不直接把 state 丟出去呢?

原因是因為透過 reactive 包裝的資料,是透過解構的方式取出的話,會失去它的連動特性,所以這個時候在需要透過 toRefs 的包裝,來讓它維持資料的連動,最後把我定義好的 count 這個資料放到畫面上

toRefs 詳情可以參考文件 https://v3.cn.vuejs.org/api/refs-api.html#torefs

現在的畫面長這樣

假設今天我直接增加一個 function去修改我拿到的count的話

<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state } = mapStore;

const headleClick = () => {
state.count++;
};

return {
...toRefs(state),
headleClick,
};
},
};
</script>
<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button @click="headleClick">add</button>
<button>remove</button>
</div>
</template>

你 click 它 log 會跳出說這個資料它是唯獨的,不可以修改

這是因為我們在 store/index.js 裡面透過readonly回傳的,所以我們不能這樣修改,readonly的好處就是要避免開發者直接修改資料,所以我們應該另外去定義可以修改的資料的 function,讓資料歸資料,修改交給其他方法去做。

我們在 store/index.is 上面新增了add 跟 remove 的兩個 functino

import { reactive, readonly } from "vue";

const state = reactive({
count: 0,
});

const addCount = () => {
state.count++;
};

const removeCount = () => {
state.count--;
};

export default {
state: readonly(state),
addCount,
removeCount,
};

然後把 function 取出來就可以掛在我們的 button,就可以成功的修改資料

<script>
import { inject, toRefs } from "vue";
export default {
setup() {
const mapStore = inject("mapStore");
const { state, addCount, removeCount } = mapStore;
return {
...toRefs(state),
addCount,
removeCount,
};
},
};
</script>
<template>
<div class="content">
<h1>count: {{ count }}</h1>
<button @click="addCount">add</button>
<button @click="removeCount">remove</button>
</div>
</template>

透過這樣的方式我們就可以不需要透過 Vuex 的方式來共用資料,你可能會問說如果要像 actions 一樣處理非同步的 API 要怎麼辦 ?

一樣我們可以把非同步放到store/index.is裡面,這邊我就寫一個大概的示範

const fetchUserData = async () => {
try {
const res = await axios.get("https://www.api.com/api/user");
const { name, age, address } = res.data;
state.name = name;
state.age = age;
state.address = address;
} catch (error) {
state.errorMessage = "This user is not found!";
}
};

這邊其實我們就把store/index.is 給當成Vuex使用那樣來使用,有state、actions/mutations,如果你想要有getter的功能的話也可以使用 computed 來達成。

import { computed, reactive, readonly } from "vue";

const userInfo = computed(()=> {
return `Info: ${state.name}, ${state.age}, ${state.address}`
})

export default {
state: readonly(state),
addCount,
removeCount,
userInfo
};

這也是很多人在說為什麼使用Vue3的時候可能不需要在使用Vuex的原因,因為我們就可以利用Provide / inject來達成使用Vuex管理資料的作法。

我自己的使用下來歸納幾個想法

  1. Provide / inject 的使用法很適合用在小專案或是小區塊的功能開發上使用。
  2. 在大型專案的時候還是主要會以Vuex作為管理狀態的主選擇,Provide / inject為輔助。
  3. Vuex之中有許多功能是Provide / inject沒有的 ( 詳請可以參考( https://next.vuex.vuejs.org/ )

最後殺雞焉用牛刀,我們不管用哪個都沒有對錯,就只有適不適合而已,今天在這邊介紹了如何使用 Provide / inject,下一篇我們來談談 Vue3 使用 Vuex 的一些進階的用法。

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

那如果對於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,那就寫兩行!