vuex学习

Vuex是做什么的?

  • 官方解释:Vuex是一个专为Vue.js应用程序开发的状态管理模式

    • 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
    • Vuex也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。
  • 状态管理到底是什么?

    • 状态里模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透
    • 其实,可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面
    • 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用
    • 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?

管理什么状态呢?

  • 有什么状态需要我们在多个组件间共享呢?
    • 如果做过大型开发,一定遇到过多个状态,在多个界面间的共享问题
    • 比如用户的登陆状态、用户名称、头像、地理位置等
    • 比如商品的收藏、购物车中的物品等等
    • 这些状态信息,我们都可以放在同一的地方,对他进行保存和管理,而他们还是响应式的(待会儿我们就可以看到代码了)

单界面状态管理

  • 我们知道,在单个组件中进行状态管理是一件非常简单的事情

    • 看下图:data中的一些数据,会保存自己的状态,在view中进行展示,view中会有一些动作,改变这个状态,改变后的值又会渲染到我们的view中

      image-20210824231620225

image-20210824231923185

多页面状态管理

  • 共同实现counter的改变和message的引用
  • 以前的写法:

App.vue

<template>
<div id="app">
<h2>{{message}}{{counter}}</h2>
<button @click="counterAdd" style="margin-right: 10px">+</button>
<button @click="counterDel">-</button>
<hello-vuex :message="message" :counter="counter"/>
</div>
</template>
<script>
import HelloVuex from "@/components/HelloVuex";
export default {
name: 'App',
data() {
return {
message: '计数器:',
counter: 0
}
},
components: {
HelloVuex
},
methods: {
counterAdd() {
this.counter++
},
counterDel(){
this.counter--
}
}
}
</script>

HelloVuex.vue

<template>
<div>
<h2>{{message}}{{counter}}</h2>
<button @click="counterAdd" style="margin-right: 10px">+</button>
<button @click="counterDel">-</button>
</div>
</template>
<script>
export default {
name: "HelloVuex",
props: {
message: String,
counter: Number
},
methods: {
counterAdd(){
this.$parent.counterAdd()
},
counterDel(){
this.$parent.counterDel()
}
}
}
</script>
  • 用了Vuex后的写法

App.vue

<template>
<div id="app">
<h2>{{$store.state.message}}{{$store.state.counter}}</h2>
<button @click="$store.state.counter++" style="margin-right: 10px">+</button>
<button @click="$store.state.counter--">-</button>
<hello-vuex/>
</div>
</template>
<script>
import HelloVuex from "@/components/HelloVuex";
export default {
name: 'App',
components: {
HelloVuex
}
}
</script>

HelloVuex.vue

<template>
<div>
<h2>{{$store.state.message}}{{$store.state.counter}}</h2>
<button @click="$store.state.counter++" style="margin-right: 10px">+</button>
<button @click="$store.state.counter--">-</button>
</div>
</template>
<script>
export default {
name: "HelloVuex"
}

安装Vuex

  1. src目录下创建store目录,新建index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // 默认执行Vuex中的install方法
export default new Vuex.Store({
state: { // 保存状态,存放一些对象
message: '计数器:',
counter: 0
},
mutations: { // 提交给Devtools,以便对提交更做跟踪,只能跟踪同步操作
},
actions: { // 触发对state的更改,支持异步操作,mutations也可以处理,但是mutations更推荐同步操作
},
modules: {
}
})
  1. 在main.js中引用并挂载到Vue中
new Vue({
router,
store, // 类似于VueRouter,会执行Vue.prototype.$store = store,以便在全局都能拿到$store
render: h => h(App)
}).$mount('#app')

Vuex的使用

image-20210825002035500

  • 以上图示可知官方是并不推荐我们直接修改$store.state.x的,要么通过Actions,要么通过Mutations

  • mutations的使用

    index.js

    mutations: {
    increment(state){
    state.counter++
    },
    decrement(state){
    state.counter--
    }
    }

    App.vue

    <template>
    <div id="app">
    <h2>{{$store.state.message}}{{$store.state.counter}}</h2>
    <button @click="addCount" style="margin-right: 10px">+</button>
    <button @click="decCount">-</button>
    <hello-vuex/>
    </div>
    </template>
    <script>
    import HelloVuex from "@/components/HelloVuex";
    export default {
    name: 'App',
    methods: {
    addCount(){
    this.$store.commit('increment')
    },
    decCount(){
    this.$store.commit('decrement')
    }
    },
    components: {
    HelloVuex
    }
    }
    </script>

Vuex核心概念

  • Vuex有几个比较核心的概念:
    • State:保存共享状态的地方
    • Getters:类似于组件中的计算属性
    • Mutation:提交,同步
    • Action:异步,调用Mutation
    • Module:专门化分一些模块 ,再在这些模块中进行一些相关数据的保存

State

单一状态树

  • 在开发中永远只创建一个Store对象:
    • 如果状态信息保存到多个Store对象中,那么之后的管理和维护都会变得特别困难
    • 所以Vuex也使用了单一状态树来管理应用层及的全部状态
    • 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试中,也可以非常方便的管理和维护

Getters

基本使用

  • 在开发中,又时候我们需要从store中获取一些state变异后的状态:

    • 比如在HelloVuex这个模板中,使用刚刚counter的平方:

    index.js

    getters: {
    powerCounter(state){
    return state.counter * state.counter
    }
    }

    HelloVuex.vue

    <h2>{{$store.state.message}}{{$store.getters.powerCounter}}</h2>

作为参数或传递参数

index.js

state: { // 保存状态,存放一些对象
message: '计数器:',
counter: 0,
students: [
{id: 1, name: 'Elian', age: 18},
{id: 2, name: 'Jack', age: 21},
{id: 3, name: 'Tom', age: 22},
{id: 4, name: 'Jay', age: 19}
]
},
mutations: {
increment(state){
state.counter++
},
decrement(state){
state.counter--
},
addCount(state, num) {
state.counter += num
}
},
actions: {
},
getters: {
powerCounter(state){
return state.counter * state.counter
},
student(state) {
return state.students.filter(s => s.age < 20)
},
studentCount(state, getters){ // 作为参数
return getters.student.length
},
studentMoreArgs(state){ // 传递参数
return age => state.students.filter(s => s.age > age)
}
},
modules: {
}

App.vue

<template>
<div>
<h2>{{$store.state.message}}{{$store.getters.powerCounter}}</h2>
<button @click="addCounter" style="margin-right: 10px">+</button>
<button @click="decCounter">-</button>
<button @click="addCount(5)" style="margin-right: 10px">+5</button>
<button @click="addCount(10)" style="margin-right: 10px">+10</button>
<button @click="decCounter">-</button>
</div>
</template>
<script>
export default {
name: "HelloVuex",
methods:{
addCounter(){
this.$store.commit('increment')
},
decCounter(){
this.$store.commit('decrement')
},
addCount(count) {
this.$store.commit('addCount', count)
}
}
}
</script>

Mutations

  • Vuex的store状态更新的唯一方式:提交Mutation

  • Mutation主要包括两部分:

    • 字符串的事件类型(type)
    • 一个回调函数(handler),该回调函数的第一个参数就是state
  • mutation的定义方式:

mutations: {
increment(state){
state.count++
}
}
  • 通过mutation更新
increment: function() {
this.$store.commit('increment')
}
  • 传递参数,在通过mutation更新数据的时候,有可能希望携带一些额外的参数

    • 参数被称为是mutation的载荷(payload)

    mutation中的代码

    addCount(state, num) {
    state.counter += num
    }

    vue中的代码

    addCount(count) {
    this.$store.commit('addCount', count)
    }
  • mutation的提交风格

    • 上面通过commit进行提交是一种普通的方式
    • Vue还提供了另外一种风格,它是一个包含type属性的对象
    • mutation中的处理方法是将震哥哥commit的对象作为payload使用

mutation中的代码

addCount(state, payload) {
state.counter += payload.count
}

vue中的代码

addCount(count) {
// 特殊的提交风格
this.$store.commit({
type: 'addCount',
count
})
}
  • mutation响应规则

    • Vuex的store中的state是响应式的,当state中的数据发生改变时,Vue组件会自动更新。
    • 这就要求我们必须遵守一些Vuex对应的规则
      • 前提在state中初始化好所需要的属性
      • 当给state中的对象添加新属性时,使用下面的方式:
        • 方式一:使用Vue.set(obj, 'newProp', 123)
        • 方式二:用新对象给旧对象重新赋值
    1. 这些属性,只要一开始就被定义到state里面了,这些属性都会被加入到响应式系统中,而我们的响应式系统会监听属性的变化,当属性发生变化时,会通知所有界面中用到属性的地方,让界面发生刷新
    2. 当通过state.info['address'] = '北京'这种方式添加的时候,界面不会监听这个属性
    3. 但是开发中,确实希望能动态增加一些被监听的属性,就需要用Vue.set(对象, '属性', 值),这个地方类似于开始讲的[Vue中那些函数是响应式的] (https://www.cnblogs.com/coderElian/p/15126495.html#数组中哪些函数是响应式的)中数组使用set等方法实现响应式类似:Vue.set(state.info, 'address', '北京'),Vue.delete('对象', '属性')
  • mutation常量类型

    • 我们来考虑下面的问题:

      • 在mutation中,我们定义了很多事件类型(也就是其中的方法名称)
      • 当我们的项目增大时,Vuex管理的状态越来越多,需要更新状态的情况越来越多,那么意味着Mutation中的方法越来越多
      • 方法过多时,使用者需要花费大量的经历去记住这些方法,甚至是多个文件间来回切换,查看方法名称,甚至如果不是赋值的时候,可能还会出现写错的情况
    • 在store下创建mutation-types.js文件

      • 将所有用到的方法名字修改成如下形式:

        export const INCREMENT = 'increment'
        export const DECREMENT = 'decrement'
        export const ADDCOUNT = 'addCount'
      • 在mutation中定义方法时,全部改成:

        import * as types from './mutation-types'
        ...
        mutations: {
        [types.INCREMENT](state){
        state.counter++
        },
        [types.DECREMENT](state){
        state.counter--
        },
        [types.ADDCOUNT](state, payload) {
        state.counter += payload.count
        }
        },
      • 在vue文件中使用的时候也全部都用这种形式去调用mutation:

        import * as types from "@/store/mutation-types";
        ...
        methods: {
        addCount(){
        this.$store.commit(types.INCREMENT)
        },
        decCount(){
        this.$store.commit(types.DECREMENT)
        }
        },

actions

  • 通常情况下,Vuex要求我们Mutation中的方法必须是同步方法

  • 如果一定要用异步的时候怎么办呢?

    • Action类似于Mutation,但是是用来代替Mutation进行异步操作的
  • action的基本使用:

    index.js中actions

    actions: {
    updateInfoAsync(store, payload) {
    setTimeout(() => {
    store.commit({
    type: types.ADDCOUNT,
    count: payload.count
    })
    payload.success()
    },1000)
    }
    },

    vue中使用

    methods:{
    ...
    addCounterAsync(count) {
    this.$store.dispatch('updateInfoAsync', {
    count,
    success: () => {
    console.log('success');
    }
    })
    }
    }
  • 更加优雅的写法:

    index.js中actions修改为:

    updateInfoAsync(store, count) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    store.commit({
    type: types.ADDCOUNT,
    count: count
    })
    resolve('success')
    },1000)
    })
    }

    vue中使用

    addCounterAsync(count) {
    this.$store.dispatch('updateInfoAsync', count).then( result =>{
    console.log(result)
    })
    }

Modules

  • 认识Module:

    • Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理
    • 当应用变得十分复杂的时候,store对象就有可能变得相当臃肿
    • 为了解决这个问题,Vuex允许我们将store分割成模块,而每个模块拥有自己的state、mutations、action、getters等
    const moduleA = {
    state: {
    name: 'Elian'
    },
    mutations: {
    updateName(state, payload) {
    state.name = payload.name
    }
    },
    actions: {
    updateNameAsync(context, name) {
    return new Promise((resolve, reject) => {
    setTimeout(()=>{
    context.commit({
    type: 'updateName',
    name
    })
    resolve('success')
    }, 1000)
    })
    },
    updateCountAsync(context, count) {
    // console.log(context)
    return new Promise((resolve, reject) => {
    setTimeout(()=>{
    context.commit({
    type: types.ADDCOUNT, // 调用父级的Actions
    count
    })
    resolve('success')
    }, 1000)
    })
    }
    },
    getters: {
    fullname(state) {
    return state.name + '·Alan'
    }
    }
    }
    export default new Vuex.Store({
    modules: {
    a: moduleA
    }
    })

    vue中使用

    {{$store.state.a.name}}
    <button @click="updateName">修改名字</button>
    {{$store.getter.fullename}}
    ...
    methods: {
    updateName() {
    this.$store.commit({
    type: 'updateName',
    name: 'Alan'
    })
    },
    updateName2() {
    this.$store.dispatch('updateNameAsync', 'Alan').then(result=>{
    console.log(result);
    })
    },
    updateCountAsync(count) {
    this.$store.dispatch('updateCountAsync', count)
    }
    }
posted @   coderElian  阅读(46)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示