在Vuex有四大物件元素,分別是State、Mutations、Getters、Actions

延續Vuex系列筆記 如果要進行非同步的AJAX,就要在Actions裡面操作

例如我們在store.js裏面用fetch,去AJAX(舉例說明而已)

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
 state:{
 todos:[ ],
 },

 actions:{
 ajaxTodos(store){
 fetch(`$(url)/todos`)
 .then(re => re.json())
 .then(ajax到的東西 => {
 //呼叫某個函式更新state
 })

 }
 }
});

export default store;

可能會是這樣的寫法 (這裡寫在actions裡面的方法,函式第一個參數其實不是store,這裡只是舉例,下面會說明) 這邊會產生個問題,假如我promise後要更新state的值... Actions無法直接更新、改動states

要改動更新state,只能用寫在Mutations的方法去改動他,我們無法在actions寫什麼

this.$store.state.todo = xxxxxx

這樣的code,這樣是錯誤的,我們必須在actions去commit、觸發Mutations的方法。

所以Mutations會這樣寫,Mutations有個方法可以改變state的值


const store = new Vuex.Store({
 state:{
 todos:[ ],
 },
 mutations:{
 changeTodos(state,data){
 state.todos = data
 }
 },
 actions:{
 ajaxTodos(store){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
  //呼叫某個函式更新state
 //store.commit('mutations方法名稱’,參數例如ajax到的東西)
 [store.commit](http://store.commit)('changeTodos’,json)
 //我們傳進去的json會對應到觸發changeTodos的第二個參數data
 })
 }
 }
});

基本上也是用commit去觸發

commit.('mutations方法名稱',參數)

第一個參數是方法的名稱,第二個是要傳給mutations該方法函式處理的參數

那mutations中,被觸發的函式要怎麼接收該參數呢? 很簡單,第一個參數一定是state,那就放在第二個參數就好了

那我們要怎麼在(.vue)組件去呼叫這個actions呢? 目前的定義都是在vuex的store.js裡面,例如說在app.vue組件的mounted( ),等vue的DOM元素完全掛載後,立刻AJAX觸發這個在vuex裡面actions這個AJAX函式呢?

app.vue

import { mapState, mapMutations, mapGetters } from 'vuex’;

export default {
 mounted(){
 this.$store.dispatch('ajaxTodos')
  }
}

一樣,因為有在amin.js全域註冊Vuex,所以個組件都被動態注入this.store,所以我們開頭就是this.store,值得注意的是... 在組件內要觸發Actions的方法,並不是commit(‘名稱') 而是dispatch(‘vuex的actions函式名稱’)

this.$store.dispatch(‘vuex的actions函式名稱’)

所以這裡才寫

this.$store.dispatch('ajaxTodos')

用dispatch去觸發actions的方法ajaxTodos進行AJAX 然後ajaxTodos方法AJAX到資料後執行mutations的changeTodos方法改變state的資料

這邊再次提醒: mutations內不能做非同步的操作 actions內不能直接修改 state內的值

正常流程是: actions非同步 => 獲得資料後commit觸發mutations(並傳值給mutations) => mutations再去改動state內的值

還有...在(.vue)組件內: 觸發mutations內定義方法用commit('函式名稱’) 觸發actions內定義方法用dispatch('函式名稱’) actions要用dispatch actions要用dispatch actions要用dispatch 別和commit搞混囉!

接著拉回來看一開始store.js的部分

 actions:{
 ajaxTodos(store){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
 [store.commit](http://store.commit)('changeTodos’,json)
 })
 }
 }

不像mutations內定義的方法函式,第一個參數必定是state actions傳值進去的是store當參數? 恩... ....... …...? 其實不對(上面筆記有說,寫在actions裡面的方法,函式第一個參數其實不是store,這裡只是舉例)

真正傳值進去的是context, context(上下文)指的是函式在被呼叫執行時,所處的物件環境。而這裡傳值進去的contet參數有內含store內全部的東西。 那和store傳進去有什麼差別? 這是因為context不是只含store,它還包含其他東西,也就是更大,那這些其他東西,在之後模組化筆記會介紹。

context包含store,store不等於context。

所以這裡要改成

 actions:{
 ajaxTodos(context){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
 context[.commit](http://store.commit)('changeTodos’,json)
 })
 }
 }

這樣才對,但還可以更精簡 ES6的解構賦值可以進行函式方法的參數解構 如果傳進context只是為了使用context裡面的commit,有些人會在ajaxTodos(context) 的參數就把commit解構傳進去

寫成這樣

 actions:{
 ajaxTodos({commit}){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
  commit.('changeTodos’,json)
 })
 }
 }

這樣更精簡,等於傳入context的store的commit

另外,我們在vue組件也可以傳入其他參數、物件進去actions裡得方法 就和 commit( )可以透過傳入第二個參數進 mutations 的方法一樣

dispatch( '函式名稱',參數 )

dispatch 第二個參數對應 actions 該函式方法的第二個參數

例如 app.vue 改成

import { mapState, mapMutations, mapGetters } from 'vuex’;

export default {
 mounted(){
 this.$store.dispatch(‘ajaxTodos’,{id:100})
 }
}

第二個參數以後的參數可以傳值傳物件,這裡我傳物件{ id:100 }

store.js

actions:{
    ajaxTodos({commit},playload){
      fetch(`$(url)/todos`)
      .then(ajax到的東西 => {
         commit.('changeTodos’,json)
         console.log(playload.id))
      })
    }
}

這麼一來,當ajax到指定的json時,就會console.log印出傳進去的物件屬性id,也就是印出100

既然可以傳物件,那(.vue)組件的dispatch也可以改成只傳一個物件

dispatch({
 type:'函式名稱’,
 //其他屬性:其他值,
})

type 必須是函式名稱,其他屬性名稱和值可以自訂 所以上面傳id屬性的寫法就要改成

app.vue

import { mapState, mapMutations, mapGetters } from 'vuex’;

export default {
 mounted(){
 this.$store.dispatch({
 type:'ajaxTodos',
 id:100
 })
 }
}

store.js一樣,不用修改

actions:{
 ajaxTodos({commit},playload){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
 commit.('changeTodos’,json)
 console.log(playload.id))
 })
 }
}

當然囉Vuex也有替Actions提供自己的方法mapActions 要使用一樣在app.vue組件import引入

import mapActions from 'vuex’;

要和其他vuex方法一起引入就寫成這樣

import { mapState, mapMutations, mapGetters, mapActions } from 'vuex’;

mapActions是提供給methods的方法 所以以上面的app.vue範例來說會改成這樣

import { mapState, mapMutations, mapGetters, mapActions } from 'vuex’;

export default {
 methods: mapActions(['ajaxTodos’]),
 mounted(){
 this.ajaxTodos({ id:100})
 }
}

mapActions傳入陣列,透過陣列中的字串(vuex的Actions方法名稱)查找到store.js該方法

在mounted()掛載我們可以透過this.ajaxTodos()去觸發這個Actions方法

因為透過字串去查找Vuex的方法名我們已經寫在methods: mapActions 所以這時在mounted( ),我們要傳給該方法函式的值就可以當成第一個參數傳入囉!

當然mapActions不只能傳陣列,也可以傳物件,物件屬性名稱自取,值一樣是Vuex該方法的名稱 例如:

import { mapState, mapMutations, mapGetters, mapActions } from 'vuex’;

export default {
 methods: mapActions({
 //自訂屬性名:’vuex該函式名稱'
 thisIsSuperAJAX:'ajaxTodos'
 }),
 mounted(){
 this.thisIsSuperAJAX({ id:100})
 }
}

既然vuex的Actions內,我們定義的方法ajaxTodos變成屬性的值 那麼要取用它當然是取用屬性名稱 所以mounted就取用this.thisIsSuperAJAX囉!

那在vuex的actions方法,如果想在actions方法內調用另一個actions方法,該怎麼寫呢? 那我們傳值進去的參數就要多一個dispatch方法 但是這個dispatch,和我們傳進去的commit一樣是來自context的方法,所以我們可以這樣寫

store.js

actions:{
 ajaxTodos({commit,dispatch},playload){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
 commit.('changeTodos’,json)
 console.log(playload.id))
 //dispatch(‘其他actions內的方法’)
 dispatch('otherAJAX')
 })
 },
 otherAJAX({commit}){
 // 略
 }
}

透過傳入dispatch,在actions方法裡去 dispatch觸發其他actions方法囉!

而除了commit、dispatch,還可以帶入其他的store參數,畢竟context包含store,store不等於context。 所以我們可以在函式的參數解構其他來自context的store參數也是OK的!

actions:{
 ajaxTodos({commit,dispatch,state,getters},playload){
 fetch(`$(url)/todos`)
 .then(ajax到的東西 => {
 commit.('changeTodos’,json)
 console.log(playload.id))
 })
 },
 otherAJAX({commit}){
 // 略
 }
}