[vue]vuex的應用

  • 3854
  • 0
  • vue
  • 2017-01-28

自我筆記vuex的應用和一些自我心得。

前言

這篇希望能清楚的說明,為何要使用vuex,如何使用vuex,更希望能把vue這個優秀的框架介紹給大家,但是需注意一下,vuex在在任何畫面一個動作的話,用eventbus會簡單許多,用vuex會較複雜,要新增一個action皆需要新增多個地方,來達成觸發action和取得state或取得getter的動作,所以在決定是否要套用vuex之前,需仔細思考幾點情境,很多情況下或許你不需要vuex。

  1. 專案的組件溝通複雜
  2. 在開發前端是多人協同合作

導覽

  1. vuex的概念
  2. vuex的進化
  3. 最基本vuex的做法
  4. 結論

 

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的討論就留待下篇吧,以上如果有任何想法,再請提出來討論。