笔记整理 vuex

346 阅读5分钟

初识 vuex

写在前面

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

我为什么使用vuex

当我们的应用遇到多个组件共享状态时 Vuex 可以帮助我们管理共享状态

本笔记只做参考, 官方学习入口点击这里

不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。

一个简单地 vuex

预热前奏

//  store.js
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)

const state = {} // 数据仓库
const mutations = {}    //  操作方法
const actions = {}  //  操作方法命令
const getters = {}  //  数据计算

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

这几个属性是干啥玩意儿呢?

state 仓库数据,用于存储数据

mutations 操作仓库数据的方法集合,建议用于对仓库单个数据的操作

actions 派发事件 | 可执行的其他操作 | 可用异步代码或多个事件派发【简言之: 生而为数据的复杂操作】

getters 仓库数据的计算属性,形如组件中的 computed 的使用

以上是一个基本的 vuex 仓库,要想在项目中使用,我们还需要在 main.js 中引入并挂载

//  main.js
import store from "./store.js"  //  引入

new Vue({
  store,    //  挂载
  render: h => h(App)
}).$mount("#app");

为了更加清晰地使用vuex,我们初步对文件代码进行分离

分离 & 使用

分离

# 目录
|-- store
    |-- index.js
    |-- state.js
    |-- mutations-type.js
    |-- mutations.js
    |-- actions.js

index.js 使用 vuex 入口

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import * as getters from './getters'
import * as actions from './actions'

Vue.use(Vuex)

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

state.js 数据集结地

const state = {
    list: [],
    tempList: []
}

export default state

mutations-type.js 事件名

export const SET_LIST = 'SET_LIST'
export const SET_TEMP_LIST = 'SET_TEMP_LIST'

mutations.js 管家

import * as types from './mutations-type'
const mutations = {
    /*************************************  
    ***   变更state中的某数据状态
    *************************************/
    [types.SET_LIST](state, list) {
        state.list = list
    },
    [types.SET_TEMP_LIST](state, list) {
        state.tempList = list
    }
}
export default mutations

actions.js 带Buff的管家

import axios from "axios";
import * as types from "./mutations-type";
export const actions = {
    /****************************************   
    ***   可包含任何异步请求
    ***   多重命令mutation, 并非直接变更数据
    ****************************************/
    async getListAndSet({ commit }, queryParams) {
        const ret = await axios.get('api/test/test1', {
            params: queryParams
        }).then(res => res.data.data)
        commit(types.SET_LIST, ret)
        commit(types.SET_TEMP_LIST, ret)
        localStorage.setItem('_list', ret)
    }
}
    

getters.js 数据计算输出

export const list = state => state.list
export const tempList = state => state.tempList
export const whiteUser = state => state.list.filter(item => (/白/g).test(item.nickname)) 

使用 & 辅助函数

.vue 中使用 vuex 数据, 我们可以在计算属性(computed)中先定义

computed: {
    list() {
        return this.$store.getters.list;
    },
    tempList() {
        return this.$store.getters.tempList;
    },
    whiteUser() {
        return this.$store.getters.whiteUser;
    }
}

再后来于组件中正常访问即可

<template>
    <div class = "list">
        <div class="item" v-for="(user, index) in list" :key="index"> {{ user.nickname }} </div>
    </div>
</template>

组件中提交 mutations & 分发 actions

export default {
    methods: {
        setListData() {
            const data = [
                { nickname: '小白1', age: 18 }, 
                { nickname: '小白2', age: 17 }, 
                { nickname: '小黑', age: 19 }
            ]
            // 组件中 提交 mutations
            this.$store.commit("SET_LIST", list);
        },
        getListAndSetData() {
            // 组件中 分发 actions 
            this.$store.dispatch("getListAndSet", {
                page: 1,
                limit: 20
            });
        }
    }
}

辅助函数 映射 vuex 数据/方法到局部组件中【喜欢就用: 推荐

  • mapMutations
  • mapActions
  • mapGetters
  • ...
import { mapGetters, mapMutations, mapActions } from "vuex";
export default {
    methods: {
        setListData() {
            const data = [
                { nickname: '小白1', age: 18 }, 
                { nickname: '小白2', age: 17 }, 
                { nickname: '小黑', age: 19 }
            ]
            this.SET_LIST(list);
        },
        getListAndSetData() {
            this.getListAndSet({
                page: 1,    
                limit: 20
            });
        },
        //  mapMutations & mapActions 都在 methods 中进行映射
        ...mapMutations(["SET_LIST"]),
        ...mapActions(["getListAndSet"])
    },
    computed: {
        //  mapState & mapGetters 都在 computed 中进行映射
        ...mapGetters(["list", "tempList", "whiteUser"])
    }
}

辅助函数参数还可以是对象

export default {
    methods: {
        setListData() {
            const data = [
                { name: '小白1', age: 18 }, 
                { name: '小白2', age: 17 }, 
                { name: '小黑', age: 19 }
            ]
            this.setList(data)    //  提交 mutations 设置列表数据
        },
        //  辅助函数接收对象形式,其它函数形式也是如此
        ...mapMutations({
            setList : 'SET_LIST',
        })
    }
}

如果项目够大/复杂 vuex 的 Module 就可以发挥巨大作用

模块化(全局命名空间)

# 基本目录结构
|-- store
    |-- modules
        |-- global.js
        |-- test.js
    |-- index.js
    |-- types.js

# 当然, 我们可以细分每个模块

store 入口文件

import Vue from 'vue'
import Vuex from 'vuex'
// 管理模块
import test from './modules/test'
import global from './modules/global'

Vue.use(Vuex)
const store = new Vuex.Store({
  modules: {
    test,
    global
  },
})
export default store;

types.js

export const SET_LIST = 'SET_LIST';
export const SET_TEMP_LIST = 'SET_TEMP_LIST';

// global
export const SET_TOKEN = 'SET_TOKEN'

modules/test.js

//  test module 
import * as types from '../types';
import axios from "axios"

const state = {
    list: [],
    tempList: []
}

const mutations = {
    [types.SET_LIST](state, list) {
        state.list = list
    },
    [types.SET_TEMP_LIST](state, list) {
        state.tempList = list
    }
}

const actions = {
    async getListAndSet({ commit }, queryParams) {
        const ret = await axios.get('api/test/test1', {
            params: queryParams
        }).then(res => res.data.data)
        commit(types.SET_LIST, ret)
        commit(types.SET_TEMP_LIST, ret)
        localStorage.setItem('_list', ret)
    }
}

const getters = {
    list: state => state.list,
    tempList: state => state.tempList,
    whiteUser: state => state.list.filter(item => (/白/g).test(item.nickname))
}

export default {
    state,
    actions,
    getters,
    mutations
}

全局命名空间 的模块化,我们可以按照以往方式正常获得属性或方法, 因为它们都默认被注册于全上.


考虑模块理应具有封装/复用性, 不喜欢命名上有所冲突, 所以

模块化 & namespace

为 vuex 模块添加命名空间时,我们需要探讨的几个问题

  • 如何给 vuex 模块添加命名空间
  • 在带命名空间的模块中访问全局命名空间内容
  • 添加了命名空间的模块在组件中访问方式

给vuex模块添加命名空间&访问全局命名空间内容

/**
 * 添加命名空间的vuex模块的actions&getters的参数对象选项中会自动添加 rootState/rootGetters 方便我们访问全局属性
 * 添加命名空间的vuex模块的actions中想要访问全局下的 dispatch 或 commit, 需要赋予第三个参数 { root: true } 表示授权
 */

import * as types from '../types';
import axios from "axios"

const state = {
    listNameSpace: []
}

const mutations = {
    ["SET_LIST"](state, list) {
        state.listNameSpace = list
    }
}

const actions = {
    // 带命名空间的 module 中 dispatch 和 commit 都被局部化,可以通过第三个参数对象 {root: true} 访问
    // 参数对象中会自动添加参数 rootState rootGtters, 分别表示全局下的数据 
    async getListAndSetNamespace({
        dispatch,
        commit,
        rootState,
        rootGetters
    }, queryParams) {
        const ret = await axios.get('api/test/test1', {
            params: queryParams
        }).then(res => res.data.data)
        commit('SET_LIST', ret) //  访问局部的 test_namespace/SET_LIST 
        commit("SET_LIST", ret, { root: true }) //  访问全局下的 SET_LIST
        dispatch("getListAndSet", ret, { root: true }) //  访问全局下的 getListAndSet

        console.log('rootState: ', rootState)
        console.log('rootGetters: ', rootGetters)
    }
}

const getters = {
    // 带命名空间的 module 中 state 和 getters 都被局部化, 会自动添加 第三个参数(rootState)第四个参数(rootGetters)可以通过它们访问全局属性
    ListNameSpaceGetters(state, getters, rootState, rootGetters) {
        return rootGetters.tempList
    }
}

export default {
    namespaced: true,
    state,
    actions,
    getters,
    mutations
}

在组件中访问带命名空间的vuex模块

<template>
    <div>
        ListNameSpaceGetters: {{ ListNameSpaceGetters }}
        <van-button @click="namespaceGetDataAndSetData">局部组件的actions调用</van-button>
    </div>
</template>
import { mapGetters, mapActions } from "vuex";
export default {
  name: "test",
  data() {
    return {};
  },
  methods: {
    namespaceGetDataAndSetData() {
        this.getListAndSetNamespace()
    },
    
    ...mapActions({
        getListAndSetNamespace: 'test_namespace/getListAndSetNamespace'
    })
  },
  computed: {
    ...mapGetters({
        listNameSpaceGetters: "test_namespace/ListNameSpaceGetters"
    })
  }
};

vuex 日志使得我们可以在 console 愉快地查看对 vuex 的操作

vuex 日志

// store 入口文件中添加 4 行代码
import Vue from 'vue'
import Vuex from 'vuex'

// 管理模块
import test from './modules/test'
import global from './modules/global'

import createLogger from 'vuex/dist/logger' //  1

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production' //  2

const store = new Vuex.Store({
  modules: {
    test,
    global
  },
  //  vuex工具
  strict: debug,    //  3
  plugins: debug ? [createLogger()] : []    //  4
})

export default store;

结语

笔记仅供参考, 如有错误还请朋友们不吝指出.