這週要來介紹一個很好用的套件 - Vuex
看到名稱應該馬上可以理解他就是Vue專案在使用的套件吧xD
有看過我之前的專案開發的文章的人,應該會發現我從來沒有用過 Vuex
我也是最近這兩週才開始接觸它的~
趁著記憶猶新時趕快把它寫起來~
如果有使用不當或是有誤的地方再麻煩多指教
萬用起手式來了~
所謂的Vuex是什麼呢? 他可以做到哪些事呢?
官方是這麼介紹它的:
Vuex是一個專為Vue.js應用程序開發的狀態管理模式。
它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。
Vuex也集成到Vue的官方調試工具devtools extension,提供了諸如零配置的time-travel調試、狀態快照導入導出等高級調試功能。
blablabla...
當時看完這段介紹之後我還是不知道Vuex是在做什麼xD
沒關係我們繼續往下看:
每一個Vuex應用的核心就是store(倉庫)。
“store”基本上就是一個容器,它包含著你的應用中大部分的狀態(state)。
到這邊應該稍微可以理解原來它的用途是「管理資料狀態的容器」
原本的做法就可以在每個元件中存放資料了,那為什麼還要用Vuex呢?
通常我們的資料流向會是父傳子,上層傳至下層,保持數據單向流通
遇到需要從子層改變父層資料時可以使用 $emit()
呼叫父層方法進而改變數據
遇到兄弟同層之間的元件要互相影響時就會有困難了,我自己的做法是將資料放到父層再用父傳子的方式處理
但是不論父傳子還是兄弟互傳的做法都非常的麻煩且繁瑣
這時候Vuex就出現了!!
我們可以將需要用到傳遞的資料通通丟進Vuex管理
當元件需要使用資料時,可以使用 $store
輕鬆取得數據
且透過套件提供的 dispatch
方法,可以從任何元件去改變資料數據
那傳遞呢? 當然用不到囉~
透過Vuex我們可以把傳遞的方式取代掉,並且還是保持著資料數據單向流通
剛才有看到官方文件說「Vuex應用的核心就是store」
而 Store 就是存放資料狀態的容器
那這個容器內包含著哪些東西呢?
以上四個項目會是基本常使用到的,其他還有像是
在數據流向的部分
前端元件(component)會透過 ditpatch
呼叫 actions 方法
actions 再透過提交(commit)的方式呼叫 mutations
mutations 再做更新 state 中的資料狀態的動作
state 更新後再將數據渲染到元件中
透過這個流程達到單向數據流的操作
基本介紹之後,就先來安裝它吧~
這裡以使用 Vue Cli 專案為主來說明
使用npm安裝指令:
npm install vuex --save
官方還有提供其他方式如:
yarn add vuex
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
安裝完成之後來建立一個最簡單的Store囉~
在專案下新增一個 /store 資料夾,資料夾內再新增一個 index.js 檔:
src
└── store
└── index.js
開啟剛剛建立的 index.js 加入基本的程式碼:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
msg: "Hello Vuex!!"
},
});
在 main.js 中引用:
import store from './store'
store 要加進vue喔!!
new Vue({
...,
store,
}).$mount('#app')
上面步驟都完成之後可以來到元件中去抓看看 msg 囉~
在元件的 computed 中使用 $store.state
來取得資料數據,可以在 App.vue 中試試看是否能抓到並渲染在畫面上:
//App.vue
<template>
<div>
{{ myMsg }}
</div>
</template>
<script>
export default {
name: "App",
computed: {
myMsg() {
return this.$store.state.msg;
},
},
};
基本的 Store 就建立完成囉!!
接著我會用不包含任何功能的TodoList來介紹後面的操作
像剛才 msg 一樣,我們在 Store 中建一個TodoList:
export default new Vuex.Store({
state: {
todolist: [
{ id: 1, todo: "交女朋友", done: false },
{ id: 2, todo: "財務自由", done: false },
{ id: 3, todo: "當碼農", done: true },
],
},
});
在 App.vue 中抓值並渲染:
<template>
<div>
<ul>
<li v-for="item in myTodo" :key="item.id">
{{ item.todo }} - {{ item.done ? "已完成" : "未完成" }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: "App",
computed: {
myTodo() {
return this.$store.state.todolist;
},
},
};
</script>
出來的畫面長這樣:
前置作業完成了,再來我要加上click事件來切換 [已完成] 及 [未完成] 的狀態:
<li
...
@click="todoDone(item.id, !item.done)"
>
...
</li>
對應的 todoDone() 方法:
methods: {
//id: 要修改狀態的資料唯一碼
//done: 要修改的值
todoDone(id, done) {
},
},
再來很重要(應該從頭到尾都很重要xD)
我們要使用 dispatch
來呼叫 actions
第一個參數設定要呼叫的 actions 名稱
第二個參數設定要傳入的值,有多個值的話就用物件的方式帶入
假設要呼叫的 actions 名稱也叫 todoDone,並且將 id & done 傳進去:
todoDone(id, done) {
this.$store.dispatch("todoDone", { id, done });
},
回到 /store/index.js 加入 todoDone():
actions: {
todoDone(context, { id, done }) {
},
},
說明一下 actions 的參數結構,每個 actions 會有帶兩個參數: myActions(context, payload)
Action函數接受一個與store實例具有相同方法和屬性的context對象,因此你可以調用context.commit提交一個mutation,或者通過context.state和context.getters來獲取state和getters。
我們在 context 中可以使用這些屬性:
{
state, // 等同於`store.state`,若在模塊中則為局部狀態
rootState, // 等同於`store.state`,只存在於模塊中
commit, // 等同於`store.commit`
dispatch , // 等同於`store.dispatch`
getters, // 等同於`store.getters`
rootGetters // 等同於`store.getters`,只存在於模塊中
}
接著我們可以在 actions 中使用 commit
將要修改的數據提交給 mutations 來更新:
todoDone(context, { id, done }) {
context.commit('TODOLIST', { id, done });
},
第一個參數設定要提交給哪個 mutations 的名稱
第二個參數設定要傳入的值,有多個值的話就用物件的方式帶入
通常actions會有一些資料面的邏輯處理,例如呼叫ajax等等,邏輯處理結束後才會提交到mutations
但範例中因為沒有這層,所以看起來像是多做了一個沒有用的動作
接著可以加入 mutations:
mutations: {
TODOLIST(state, { id, done }) {
},
}
這邊使用了常量替代Mutation 事件類型:
使用常量替代mutation 事件類型在各種Flux 實現中是很常見的模式。這樣可以使linter 之類的工具發揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作者對整個app 包含的mutation 一目了然
用不用常量取決於你——在需要多人協作的大型項目中,這會很有幫助。但如果你不喜歡,你完全可以不這樣做。
說明一下 mutations 的參數結構,每個 mutations 會有帶兩個參數: myMutations(state, payload)
最後就是更新資料狀態
先抓到 state 中的 todolist:
let todolist = state.todolist;
使用陣列的 find()
方法抓到要改的那筆:
let todo = todolist.find((item) => {
return item.id === id;
});
更新 done 值:
todo.done = done;
整段會是這樣:
TODOLIST(state, { id, done }) {
let todolist = state.todolist;
let todo = todolist.find((item) => {
return item.id === id;
});
todo.done = done;
},
也可以合併成這樣:
TODOLIST(state, { id, done }) {
state.todolist.find((item) => {
return item.id === id
}).done = done;
},
到這裡就完成更新資料狀態囉!!
來看看結果吧~
先到這囉~
下週繼續來探討模組化以及其他的功能!!