Vue3-Vuex状态管理

118 阅读2分钟

一、认识应用状态管理

1. 什么是状态管理

  • 在开发中,我们会的应用程序需要处理各种各样的数据,这些数据需要保存在我们应用程序中的某一个位置,对于这些数据的管理我们就称之为状态管理

2. 如何管理状态

  • 在Vue开发中,我们使用组件化的开发方式
  • 而在组件中我们定义data或者在setup中返回使用的数据,这些数据我们称之为state
  • 在模块template中我们可以使用这些数据,模块最终会被渲染成DOM,我们称之为View
  • 在模块中我们会产生一些行为事件,处理这些行为事件时,有可能会修改state,这些行为事件我们称之为actions 61_状态管理.jpg

3. Vuex的状态管理

61_Vuex状态管理.jpg

二、Vuex的基本使用

1. 创建Store

  • 每一个Vuex应用的核心就是store(仓库)。store本质上是一个容器,它包含着你的应用中大部分的状态(state)
  • Vuex和单纯的全局对象的区别:Vuex的状态存储是响应式的;不能直接改变store的状态,改变的唯一途径就是提交(commit)mutation

2. 组件中使用Store

  • 在模板中使用
  • 在options api中使用,比如computed
  • 在setup中使用
<template>
    <div class="home>
        <h2>Home当前计数:{{ this.$store.state.counter }}</h2>
        <h2>Home当前计数:{{ storeCounter }}</h2>
        <h2>Home当前计数:{{ counter }}</h2>
    </div>
</template>
<sciprt>
    export default {
        computed: {
            storeCounter() {
                return this.$store.state.counter
            }
        }
    }
</script>
<sciprt setup>
    import { toRefs } from "vue"
    import { useStore } from "vuex"
    
    const store = useStore()
    const { counter } = toRefs(store.state)
</script>

3. 单一状态树

  • Vuex使用单一状态树,用一个对象就包含了全部的应用层级的状态
  • 每个应用将仅仅包含一个store实例

三、核心概念State

import { createStore } from "vuex"
const store = createStore({
    state: () => ({
        counter: 100,
        level: 100,
        avatarURL: "http://baidu.com"
    })
})
  • 获取状态state的方法
    • 组件获取状态
    • 计算属性获取状态
    • setup获取状态
    • mapState辅助函数获取状态
    • 在setup中使用mapState
    <template>
        <div class="home">
            <!-- 1. 在模板中直接使用多个状态 -->
            <h2>{{ $store.state.name }}</h2>
            <h2>{{ $store.state.level }}</h2>
            <h2>{{ $store.state.avatarURL }}</h2>
            <!-- 2. 计算属性(映射状态:数组语法) -->
            <h2>{{ name }}</h2>
            <!-- 3. 计算属性(映射状态:对象语法) -->
            <h2>{{ sName }}</h2>
            <!-- 4. setup计算属性 -->
            <h2>{{ name }}</h2>
            <!-- 5. setup计算属性(映射状态:对象语法) -->
            <h2>{{ cName }}</h2>
        </div>
    </template>
    <script>
        import { mapState } from "vuex"
        export default {
            computed: {
                ...mapState(["name, "level", "avatarURL"]),
                ...mapState({
                    sName: state => state.name,
                    sLevel: state => state.level
                })
            }
        }
    </script>
    <script setup>
        import { toRefs, computed } from "vue"
        import { useStore, mapState } from "vuex"
        // 1. 直接对store.state进行解构
        // const store = useStore()
        // const { name, level } = toRefs(store.state)
        // 2. mapState映射
        const { name, level } = mapState(["name", "level"])
        const store = useStore()
        const cName = computed(name.bind({ $store: store })
        const cLevel = computed(level.bind({ $store: store })
    </script>
    

四、核心概念Getters

const { createStore } from "vuex"
const store = createStore({
    state: () => ({
        counter: 100,
        level: 100,
        friends: [
            { id: 111, name: "why", age: 20 },
            { id: 222, name: "messi", age: 30 },
            { id: 333, name: "kobe", age: 35 }
        ]
    }),
    getters: {
        // 1. 基本使用
        doubleCounter(state) {
            return state.counter * 2
        },
        // 2. 在该getters属性中,获取其他的getters
        message(state, getters) {
            return `name-${state.name}; level-${state.level}; doubleCounter-${getters.doubleCounter}`
        },
        // 3. getters是可以返回一个函数的,调用这个函数
        getFriendById(state) {
            return function(id) {
                return state.friends.find(item => item.id === id)
            }
        }
    }
})
export default store
<template>
    <div class="home">
        <!-- 1. 基本使用 -->
        <h2>doubleCounter:{{ $store.getters.doubleCounter }}</h2>
        <h2>id-111:{{ $store.getters.getFriendById(111) }}</h2>
        <!-- 2. mapGetters -->
        <h2>doubleCounter:{{ doubleCounter }}</h2>
        <h2>id-111:{{ getFriendById(111) }}</h2>
        <h2>message:{{ message }}</h2>
    </div>
</template>
<script>
    import { mapGetters } from "vuex"
    export default {
        computed: {
            ...mapGetters(["doubleCounter"]),
            ...mapGetters(["getFriendById"])
        }
    }
</script>
<script setup>
    import { toRefs, computed } from "vue"
    import { useStore, mapState } from "vuex"
    // 1. 基本使用
    // const store = useStore()
    // const { message } = toRefs(store.state)
    // 2. mapGetters
    const { message: messageFns } = mapGetters(["message"])
    const message = computed(messageFns.bind({ $store: store })
</script>

五、核心概念Mutations

  • Mutation基本使用
  • Mutation携带数据
  • mapMutations辅助函数
import { createStore } from "vuex"
const store = createStore({
    state: () => ({
        counter: 100,
        level: 100
    }),
    mutations: {
        // 1. 基本使用
        increment(state) {
            state.counter++
        },
        // 2. 携带参数
        changeName(state, payload) {
            state.name = payload
        }
    }
})
export default store
<template>
    <div class="home">
        <h2>name:{{ $store.state.name }}</h2>
        <h2>level:{{ $store.state.level }}</h2>
        <button @click="increment">increment</button>
        <button @click="changeName('哈哈哈')">changeName</button>
    </div>
</template>
<script>
    import { mapMutations } from "vuex"
    export default {
        methods: {
            increment() {
                this.$store.commit("increment")
            },
            ...mapMutations(["changeName"])
        }
    }
</script>
<script setup>
    import { mapMutations, useStore } from "vuex"
    const store = useStore()
    const mutations = mapMutations(["increment", "changeName"])
    const newMutations = {}
    Object.keys(mutations).forEach(key => {
        newMutations[key] = mutations[key].bind({ $store: store })
    })
    const { increment, changeName } = newMutations
</script>

六、核心概念Actions

  • actions基本使用
    • Action提交的是mutation,而不是直接变更状态
    • Action可以包含任何异步操作
    • Action的一个参数context:context是一个和store实例均有相同方法和属性的context对象,所以我们可以从其中获取到commit方法来提交一个mutation
  • actions的分发操作
  • actions辅助函数mapActions
  • actions的异步操作
    • 我们可以通过让action返回Promise,在Promise的then中处理后续的操作
import { createStore } from "vuex"
const store = createStore({
    state: () => ({
        counter: 100,
        level: 100
    }),
    mutations: {
        increment(state) {
            state.counter++
        },
        changeName(state, payload) {
            state.name = payload
        }
    },
    actions: {
        incrementAction(context) {
            context.commit("increment")
        },
        changeNameAction(context, payload) {
            context.commit("changeName", payload)
        },
        fetchAction(context) {
            return Promise(async (resolve, reject) => {
                const res = await fetch(url)
                const data = awati res.json()
                
                context.commit("changeName", data)
                resolve("aaa")
            })
        }
    }
})
export default store
<template>
    <div class="home">
        <h2>name:{{ $store.state.name }}</h2>
        <h2>level:{{ $store.state.level }}</h2>
        <button @click="incrementAction">incrementAction</button>
        <button @click="changeNameAction('哈哈哈')">changeNameAction</button>
    </div>
</template>
<script>
    import { mapActions } from "vuex"
    export default {
        methods: {
            counterBtnClick() {
                this.$store.dispatch("incrementAction")
            },
            nameBtnClick() {
                this.$store.dispatch("changeNameAction")
            },
            ...mapActions(["incrementAction", "changeNameAction"])
        }
    }
</script>
<script setup>
    import { mapActions, useStore } from "vuex"
    const store = useStore()
    // 1. 使用默认做法
    // function incrementAction() {
    //     store.dispatch("incrementAction")
    // }
    const actions = mapActions(["incrementAction", "changeNameAction"])
    const newActions = {}
    Object.keys(actions).forEach(key => {
        newActions[key] = actions[key].bind({ $store: store })
    })
    const { incrementAction, changeNameAction } = newActions
</script>

七、核心概念Modules

1. moudule的基本使用

  • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store对象就有可能变得相当臃肿
  • 为了解决以上问题,Vuex允许我们将store分割成模块(module)
  • 每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块

2. module的命名空间

  • 如果我们希望模块具有更高的封装度和复用性,可以添加 namespaced: true 的方式使其成为带命名空间的模块