自我筆記vuex的應用和一些自我心得。
前言
這篇希望能清楚的說明,為何要使用vuex,如何使用vuex,更希望能把vue這個優秀的框架介紹給大家,但是需注意一下,vuex在在任何畫面一個動作的話,用eventbus會簡單許多,用vuex會較複雜,要新增一個action皆需要新增多個地方,來達成觸發action和取得state或取得getter的動作,所以在決定是否要套用vuex之前,需仔細思考幾點情境,很多情況下或許你不需要vuex。
- 專案的組件溝通複雜
- 在開發前端是多人協同合作
導覽
vuex的概念
vuex也是參考了redux的想法,基本想法就是單向資料流,已使數據流可預測,vuex對於檔案結構沒有任何的限制,但需遵守一個原則,也就是任何要觸發state的改變,都要去觸發action,不可以直接去呼叫mutation,action可以假想成是我們任何畫面的事件,state則可以視為綁定在畫面中的狀態,比如我有新刪修的按鈕,當我按新增的時候,可能要把刪除和修改給隱藏,然後取消和儲存的按鈕給顯示出來,按下儲存或取消的時候,又要回復成只有新刪修的按鈕,每個按鈕都會有各自的狀態,在一個複雜的畫面,這可能還只是很小的一部份,這些所有的狀態我們都會保存在一個store裡面,只是當組件越來越多的時候,整個store也相對的越來越複雜,在vuex2.1.0的版本裡面也加入了namespaced的概念,就可以讓我們用namespaced的概念來把store切割成若干個小區塊,vuex一個簡單的流程大致就是view event->vuex action->vuex mutation->vuex state->view binding change,下圖則是vuex的各種動作流程圖。
vuex的進化
先說明一下,vuex是為一個store,所以所有actions,mutations,states都可以只放在一支檔案或拆成各支檔案,再來還有可以分成module,最新的做法則是以namespaced來區隔,隨著專案複雜性的提升,我們勢必不可能全部只放在一個檔案裡面,此篇想談的是最基礎全部放在一起的做法,力求簡單好上手。
最基本vuex的做法
在此我用最簡單的例子來做說明,也就是全部放在一起的概念,例子就是按下button的時候,觸發一個action然後透過getter自動更新view,檔案結構如下,請注意一下,你也可以把所有的檔案全寫在一支index.js裡面,在此vuex並無強烈限制應該如何做。
先從routers.js說起,這支是路由設定,原始碼如下
import count from './components/Count.vue'
const routers = [{
path: '/count',
component:count
},
{
path: '/*',
redirect: '/count'
}
]
export default routers
main.js則是第一支進入點的js檔,設定在webpack裡面,這邊不多做特別說明,以免過於離題
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App'
import routers from './routers'
import store from './store'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: routers
})
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
在來看一下App.vue的部份,也就是最根層的component,大致上都會放一些共用的比如header或footer和路由
<template>
<div id="app">
<ul>
<li>
<router-link to="count">go to count</router-link>
</li>
</ul>
<router-view style="padding-top: 10px"></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style scoped>
</style>
在來就是store裡面的部份了,略短說明一下,如果要更詳細的話可以直接參考官網的說明,我們首先定義store裡面的state,這個state則是對映畫面上binding的值(並無絕對看如何設計),任何按鈕按下,想要改變畫面的狀態,則是先呼叫actions然後再觸發mutations,然後mutations改成store的state狀態,我會再定義getters來做一個中間層,取得state之後再render view,所以在component裡面,只會呼叫actions和使用getters去取得值,接著就看一下store裡面的程式碼吧
store/state.js
export default {
count: 1//只定義一個count,但畫面的狀態越來越多的時候,就會定義越來越多的值,可能是array或object,甚至是字串....
}
store/actions.js
export default {
addCount({ commit }) {
commit('addCount', 1) //呼叫mutations
}
}
store/mutations.js
export default {
addCount(state, num) {
state.count += num //改變成state.js定義的count數值去加1,num是在actions我們丟進去的1
}
}
store/getters.js
export default {
count: state => state.count //取得state裡面的內容
}
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import actions from './actions'
import state from './state'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex)
export default new Vuex.Store({
actions,
state,
mutations,
getters,
struct: true
})
最後來看一下count.vue的component吧,裡面做的事情非常簡單,就是觸發了action,然後用getters去把count+1而已
<template>
<div id='count'>
<button @click="addCount">+1</button> 累積的值{{count}}
</div>
</template>
<script>
import {
mapActions,
mapGetters
} from 'vuex'
export default {
name: 'count',
methods: {
...mapActions([
'addCount' //對應template的click觸發的action
])
},
computed: {
...mapGetters([
'count' //對應到畫面上的{{count}}
])
}
}
</script>
<style scoped>
</style>
上述完成之後,可以看到畫面如下
結論
看起來做那麼簡單的範例,為何要寫那麼多code建那麼多檔案來完成這件事,那只是為了用最簡單的example來說明,以便可以很容易自己實做出來vuex的結構,比較複雜的頁面可能光是state就好幾十個甚至破佰個,如果有上百個頁面,想必會是一場非常恐佈的災難,如果有很多頁面的話,全部放在global的話,就很難維護,那這時候我們就可以使用namespaced的方式,為了避免篇幅過長,namspaced的討論就留待下篇吧,以上如果有任何想法,再請提出來討論。