vue項目中使用Vuex(詳細教程)

vuex

什麼是vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。
可以理解爲我們項目中需要共享的一些數據,我們把它集中管理起來的容器。把這些數據稱爲狀態。比如一個用戶的用戶名,性別,權限級別等等。

是否一定要用vuex

如果我們不是一箇中大型的單頁面應用的話,沒有那麼多的狀態需要共享的話就不需要用。對於小項目我們可以只用簡單的狀態管理:

創建:simpleStore.js
// 簡單的狀態管理器
const simpleStore = {
    state: {
        token: '',
        userInfo: {
            name: '',
            sex: 1
        }
    },
    // 設置數據到本地sessionStorage
    setStateToStorage (str, obj) {
        window.sessionStorage.setItem(str, JSON.stringify(obj))
    },
    // 清除sessionStorage
    removeLocalStorage (str) {
        window.sessionStorage.removeItem(str)
    },
    // 設置用戶信息
    setUserInfo (obj) {
        this.state.userInfo = { ...obj }
        this.setStateToStorage('userInfo', this.state.userInfo)
    },
    // 獲取用戶信息
    getUserInfo () {
        return window.sessionStorage.getItem('userInfo')
    },
    // 清除用戶信息
    clearUserInfo () {
        this.state.userInfo.userName = ''
        this.state.userInfo.sex = 1
        this.removeLocalStorage('userInfo')
    }
}

export default simpleStore

引入並掛載到vue:
import simpleStore from './store/simpleStore'

Vue.prototype.$simpleStore = simpleStore

這畢竟是個簡單的狀態管理,頁面刷新就會導致保存到數據丟失,所以是需要保存到storage纔行的。

Vuex核心概念

1. state

所有需要共享的數據都存放在 state 中

定義:
new Vuex.Store({
    state: {
        token: '',
        todos: [
            { id: 1, text: '...', done: true },
            { id: 2, text: '...', done: false }
        ]
    },
})

在vue組件中使用:
// 直接調用
this.$store.state.token

// 通過計算屬性,直接用{{ token }},或者this.token就能調用
computed: {
    token () {
        return this.$store.state.token
    }
}

// 或者調用 mapState
import { mapState } from 'vuex'

computed: {
    ...mapState({
        token: state => state.token
    })
    // 或者
    ...mapState([
        'token'
    ])
}

2. getter

可以對state進行過濾與加工,可以認爲是 store 的計算屬性

定義:
new Vuex.Store({
    getters: {
        // state 作爲其第一個參數
        doneTodos: state => {
            return state.todos.filter(todo => todo.done)
        },
        // getters 可作爲第二個參數
        doneTodosCount: (state, getters) => {
            return getters.doneTodos.length
        },
        // 讓 getter 返回一個函數,來實現給 getter 傳參,對 store 裏的數組進行查詢時非常有用
        getTodoById: state => id => {
            return state.todos.find(todo => todo.id === id)
        }
        // 使用:this.$store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
    },
})

在vue組件中使用:
// 直接調用
this.$store.getters.doneTodos

// 通過計算屬性,直接用{{ doneTodosCount }},或者this.doneTodosCount就能調用
computed: {
    doneTodosCount () {
        return this.$store.getters.doneTodosCount
    }
}

// 或者調用 mapGetters
import { mapGetters } from 'vuex'

computed: {
    // getter 屬性另取一個名字,使用對象形式
    ...mapGetters({
        doneCount: 'doneTodosCount'
    })
    // 或者
    ...mapGetters([
        'doneTodos',
        'doneTodosCount'
    ])
}

3. mutations

在 Vuex 中只能通過提交 mutation 來修改 store 中的狀態,且是一個同步的過程

定義:
new Vuex.Store({
    mutations: {
        // state 作爲第一個參數,接收第二個參數爲傳入的對象
        setToken (state, obj) {
            const st = state
            st.token = obj
        }
    },
})

在需要多人協作的大型項目中,使用常量替代 Mutation 事件類型:

// 新建 mutation-types.js
export const SET_TOKEN = 'SET_TOKEN' // 設置token
// store.js
import { SET_TOKEN } from './mutation-types'

new Vuex.Store({
    mutations: {
        // state 作爲第一個參數,接收第二個參數爲傳入的對象
        [SET_TOKEN] (state, obj) {
            const st = state
            st.token = obj
        }
    },
})

在vue組件中使用:
// 直接調用
this.$store.commit('setToken', obj)

// 在 methods 裏使用 mapMutations,就可以 @click="setToken(obj)" 來調用
import { mapMutations } from 'vuex'

methods: {
    ...mapMutations({
        setToken: 'SET_TOKEN'
    })
    // 或者
    ...mapMutations([
        'setToken',
        'SET_TOKEN'
    ])
}

4. actions

通過調用 mutation 方法異步的改變 state 狀態

定義:
new Vuex.Store({
    actions: {
        // context 對象作爲第一個參數,與 store 實例具有相同方法和屬性,但不是 store 實例本身
        login (context) {
            context.commit('SET_TOKEN')
            console.log(context.state)
            console.log(context.getters)
        }
        // es2015 參數結構的方式,接收第二個對象作爲參數傳入
        login ({ commit, state, getters }, obj) {
            commit('SET_TOKEN')
        }
        // 異步操作,例:
        login ({ commit }, params) {
            const { account, pwd } = params
            // 定義一個promise
            return new Promise((resolve, reject) => {
                // 異步操作
                Vue.prototype.$apis.login({ account: account, pwd: pwd }).then(response => {
                    if (response.code === '000') {
                        const { data } = response
                        // 調用mutation
                        commit('SET_TOKEN', data.token)
                        setToken(data.token)
                        // 可以在成功後傳出數據
                        resolve(data.loginStats)
                    } else {
                        reject(response.message)
                    }
                }).catch(error => {
                    reject(error)
                })
            })
        }
        // 組合 action,利用async await和dispatch來實現多action的嵌套調用
        async actionB ({ dispatch, commit }) {
            await dispatch('actionA') // 等待 actionA 完成
            commit('gotOtherData', await getOtherData())
        }
    },
})

在vue組件中使用:
// 直接調用
this.$store.dispatch('login', obj)

// 調用 action 中的異步操作,並處理
this.$store.dispatch('login', params).then((val) => {
    this.$message.success('登錄成功!')
    val && this.$router.push('home')
}).catch(err => {
    console.log('登錄錯誤: ', err)
})

// 在 methods 裏使用 mapActions,就可以 @click="login(obj)" 來調用
import { mapActions } from 'vuex'

methods: {
    ...mapActions({
        login: 'login'
    })
    // 或者
    ...mapActions([
        'login'
    ])
}

5. modules

將 store 分割成模塊,每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊

定義:
// 定義一個 user.js 模塊
const user = {
    namespaced: false,
    state: {
        userInfo: {}
    },
    getters: {
        // rootState是根節點狀態
        getUser (state, getters, rootState) {}
    },
    mutations: {
        setUser (state) {}
    },
    actions: {
        // rootState是根節點狀態
        getUserInfo ({ state, commit, rootState }) {}
    }
}

export default user

// 在store.js中導入
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user' // 導入模塊user

Vue.use(Vuex)

export default new Vuex.Store({
    state: {},
    getters: {},
    mutations: {},
    actions: {},
    modules: {
        user
    }
})

在vue組件中使用:

默認情況下,模塊內部的 action、mutation 和 getter 是註冊在全局命名空間的,只有state註冊在自己的模塊內,調用需要加上模塊名。調用的方式:

// state
computed: {
    ...mapState({
        userInfo: state => state.user.userInfo
    })
    ...mapGetters({
        getUser: 'getUser'
    })
}
// getters
this.$store.getters.getUser
// mutations
this.$store.commit('setUser', obj)
// actions
this.$store.dispatch('getUserInfo', obj)

設置 namespaced: true 使模塊具有更高的封裝度和複用性。

這時模塊內的getter會有第四參數rootGetters,作爲訪問全局getter:

getters: {
    // rootState是根節點狀態
    getUser (state, getters, rootState, rootGetters) {}
},
actions: {
    // rootState是根節點狀態
    getUserInfo ({ state, commit, rootState, rootGetters }) {}
}

在組件中調用 commit 和 dispatch 時,需要在具體mutation和action前把模塊名加上:

// mutations
this.$store.commit('user/setUser', obj)
// actions
this.$store.dispatch('user/getUserInfo', obj)

在設置 namespaced: true 子模塊內想要調用全局的 mutation 或者 action 時需要將 { root: true } 作爲第三參數傳給 dispatch 或 commit:

// mutations
commit('SET_TOKEN', obj, { root: true })
// actions
dispatch('otherModule/getUserInfo', obj, { root: true })
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章