Vuex(一) —— 集中式的状态管理仓库

目录

  • Vue组件间通信方式回顾
    • 组件内的状态管理流程
    • 组件间通信方式
      • 父组件给子组件传值 (最简单的一种方式)
      • 子组件给父组件传值
      • 不相关组件之间传值
      • 其他常见方式($ref)
  • 简易的状态管理方案
    • 上面组件间通信方式的问题
    • 集中式的状态管理方案
  • Vuex
    • 什么是Vuex?
    • 什么情况下使用Vuex?
      • 非必要的情况不要使用Vuex
      • 中大型单页应用程序使用更好
    • Vuex核心概念回顾
    • Vuex基本结构
    • State的使用
    • Getter的使用
    • Mutation的使用
      • Mutation的调试
        • 时光旅行
        • 状态回滚
        • 提交改变
    • Actions的使用
    • Modules的使用
      • 模块定义
      • 模块注册
      • 模块使用
      • 添加命名空间
    • Vuex严格模式
    • Vuex插件
      • 插件的使用

年底最后一天来本来没有想更博客,但是留下我今年还在努力学习的印记哈哈。看了半天这居然是我第一个关于vue的博客,努力不算晚。先来对这个博客的内容进行一下梳理,有兴趣的小伙伴可以跳到自己感兴趣的地方,这个博客比较长,长文警告。

image

Vue组件间通信方式回顾

组件内的状态管理流程

Vue最核心的两个功能:数据驱动组件化

使用基于组件化的开发,可以提高开发效率,带来更好的可维护性。

new Vue({// state 组件内部都可以管理自己的内部状态data () {return {count: 0}},// view 视图,每个组件都有自己的视图,把状态绑定到视图上,当用户和视图交互的时候,可能会更改状态template: `<div>{{ count }}</div>`,// actions 行为,这里描述的是单个组件内部的状态管理,实际开发中可能多个组件可以共享状态methods: {increment () {this.count++}}
})

这里说的状态管理 —— 是通过状态,集中管理和分发,解决多个组件共享状态的问题。

  • state:状态,数据源。
  • view:视图。通过把状态绑定到视图呈现给用户
  • actions:用户和视图交互改变状态的方式
image

图中表明,状态绑定到视图上呈现给用户,用户通过与视图交互改变状态,之后改变了的状态再绑定到视图会后呈现给用户。
单向的数据流程很简单清晰,但是多个组件共享数据会破坏这种简单的结构。

组件间通信方式的回顾

大多数情况下,组件都不是孤立存在的,他们需要共同协作构成一个复杂的业务功能,在Vue中,为不同的组件关系提供了不同的通信规则。

常见的组件间通信的方式有:

父组件给子组件传值 (最简单的一种方式)
  • 父组件中给子组件通过相应属性传值
  • 子组件通过props接受数据
<!--子组件-->
<template><div><h1>Props Down Child</h1><h2>{{ title }}</h2></div>
</template><script>
export default {// 子组件中通过props来接收父组件传的值// props可以是数组也可以是对象// 如果想约定传值的类型用对象,这里title定了是string类型,如果传number类型会报错// props: ['title'],props: {title: String}
}
</script><style></style>
<!--父组件-->
<template><div><h1>Props Down Parent</h1><!--2. 使用子组件的使用通过属性给子组件传值,这里也可以是表达式,绑定data中的成员--><child title="My journey with Vue"></child></div>
</template><script>
import child from './01-Child'
export default {// 1. 注册子组件components: {child}
}
</script><style></style>
子组件给父组件传值
  • 子组件通过自定义事件,用$emit触发的时候携带参数给父组件传值
  • 父组件通过$on注册子组件内部触发的事件,并接收传递的数据,行内可以通过$event获取事件传递的参数 (事件处理函数中是不这么使用的)
<!--子组件-->
<template><div><h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1><button @click="handler">文字增大</button></div>
</template><script>
export default {// 通过props接收父组件传的默认字体大小props: {fontSize: Number},methods: {handler () {// 当点击按钮的时候,触发自定义事件enlargeText放大字体,让字体放大0.1// this是当前子组件对象,this.$emit这个是由子组件触发的自定义事件,当注册事件的时候要给子组件注册该事件this.$emit('enlargeText', 0.1)}}
}
</script><style></style>
<!--父组件-->
<template><div><!--父组件将fontSize进行绑定--><h1 :style="{ fontSize: hFontSize + 'em'}">Event Up Parent</h1>这里的文字不需要变化<!--使用子组件,通过v-on给子组件设置了自定义方法enlargeText--><child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child><child :fontSize="hFontSize" v-on:enlargeText="enlargeText"></child><!--还有一种在行内获取值的方式,获取自定义事件传递数据的时候,直接通过$event获取,这个值是触发事件传递的0.1--><child :fontSize="hFontSize" v-on:enlargeText="hFontSize += $event"></child></div>
</template><script>
import child from './02-Child'
export default {components: {child},data () {return {hFontSize: 1}},methods: {// 子组件把值传递给了父组件,父组件通过参数接收到了值,进行运算enlargeText (size) {this.hFontSize += size}}
}
</script><style></style>
不相关组件之间传值
  • 也是使用自定义事件的方式,但是因为没有父子关系,所以不能通过子组件触发传值,所以这里需要使用eventBus,即创建一个公共的Vue实例,这个实例的作用是作为事件总线,或者事件中心。
  • eventBus:创建一个Vue实例,这个实例并不是用来展示内容,所以没有传递任何的选项,我们使用他的目的是使用$emit$on,用来触发和注册事件。
  • 触发事件组件:通过$emit触发事件,并传递参数
  • 注册事件组件:通过$on注册事件,接收参数进行处理
// eventbus.js
import Vue from 'vue'
export default new Vue()
<!--组件一:触发事件-->
<template><div><h1>Event Bus Sibling01</h1><div class="number" @click="sub">-</div><input type="text" style="width: 30px; text-align: center" :value="value"><div class="number" @click="add">+</div></div>
</template><script>
// 这个组件要触发事件,将事件中心导入
import bus from './eventbus'export default {
// props参数numprops: {num: Number},// 因为props的值不能随便改动,所以传递给valuecreated () {this.value = this.num},data () {return {value: -1}},methods: {// 减值操作,判断不能为0sub () {if (this.value > 1) {this.value--// 触发bus的自定义事件numchange,并把value当参数传递出去bus.$emit('numchange', this.value)}},// 加值操作,和减值类似add () {this.value++bus.$emit('numchange', this.value)}}
}
</script><style>
.number {display: inline-block;cursor: pointer;width: 20px;text-align: center;
}
</style>
<!--组件二:定义-->
<template><div><h1>Event Bus Sibling02</h1><div>{{ msg }}</div></div>
</template><script>
// 因为要注册事件,所以将事件中心导入
import bus from './eventbus'
export default {data () {return {msg: ''}},created () {// 通过bus注册了numchange事件,事件处理函数中接收事件触发时候传递的参数,进行展示bus.$on('numchange', (value) => {this.msg = `您选择了${value}件商品`})}
}
</script><style></style>
<!--App.vue-->
<template><div id="app"><h1>不相关组件传值</h1><sibling0301 :num="num"></sibling0301><sibling0302></sibling0302></div>
</template><script>
import sibling0301 from './components/03-event-bus/03-Sibling-01'
import sibling0302 from './components/03-event-bus/03-Sibling-02'export default {name: 'App',components: {sibling0301,sibling0302,},data () {return {num: 1}}
}
</script><style></style>
其他常见方式
  • $root$parent$children$ref,通过这几种属性获取根组件成员,实现组件之间的通信。但这些都是不被推荐的方式。只有当项目很小,或者在开发自定义组件的时候,才会使用到。如果是大型项目的话,还是推荐使用Vuex管理状态。

下面举例通过$refs获取子组件的状态,其他属性可以自己查看文档。

ref的两个作用

  1. 在普通HTML标签上使用ref,用$refs获取到的是DOM对象
  2. 在组件标签上使用ref,用$refs获取到的是组件实例
<!--子组件,一个简单的自定义组件,功能是能够获取焦点的自定义文本框。-->
<template><div><h1>ref Child</h1><!--这个input标签上设置了ref属性--><input ref="input" type="text" v-model="value"></div>
</template><script>
export default {data () {return {value: ''}},methods: {focus () {// 通过this.$refs.input获取input的DOM对象,并调用其focus方法让文本框获取焦点this.$refs.input.focus()}}
}
</script><style></style>
<!--父组件,-->
<template><div><h1>ref Parent</h1><!--在子组件的标签上设置了ref--><child ref="c"></child></div>
</template><script>
import child from './04-Child'
export default {components: {child},mounted () {// 这里想要拿到子组件的话,必须要等组件渲染完毕,所以这里在mounted函数下// 这里通过this.$refs.c就是子组件对象,拿到这个对象就可以访问其属性和方法// 这里调用子组件方法让其内部获取焦点this.$refs.c.focus()// 通过value属性给文本框赋值this.$refs.c.value = 'hello input'}
}
</script><style></style>

还是一句话不建议使用,如果滥用这种方式的话可以造成状态管理的混乱。

简易的状态管理方案

上面组件间通信方式的问题

如果多个组件之间需要共享状态,使用之前演示的方式虽然都可以实现,但是比较麻烦,而且多个组件之间进行传值,很难跟踪到数据的变化。如果出现问题的话,很难定位问题。当遇到多个组件需要共享状态的时候,典型的场景如购物车,我们使用之前介绍的方案都不合适,可能会遇到以下的问题:

  • 多个视图依赖同一状态,如果多层嵌套的组件依赖同一状态,使用父子组件传值可以实现,但是非常麻烦而且不易管理。
  • 来自不同视图的行为需要变更同一状态,我们可以通过父子组件的方式对状态进行修改,或者通过事件机制来改变,或者同步状态的变化,以上这些方式非常的脆弱,通常会导致产生无法维护的代码。

集中式的状态管理方案

为了解决这些问题,我们把不同组件的共享状态抽取出来,存储到一个全局对象中并且将来使用的时候保证其实响应式的。这个对象创建好之后里面有全局的状态和修改状态的方法,我们的任何组件都可以获取和通过调用对象中的方法修改全局对象中的状态 (组件中不允许直接修改对象的state状态属性)。

把多个组件的状态放到一个集中的地方存储,并且可以检测到数据的更改,这里先不使用Vuex,我们自己先进行一个简单的实现。

  1. 创建一个全局的store.js

集中式的状态管理,所有的状态都在这里。这个模块中导出了一个对象,这对象就是状态仓库且全局唯一的对象,任何组件都可以导入这个模块使用

这里面有state,还有actionsstate是用来存储状态,actions是用户交互更改视图用的。还有一个debug的属性,方便开发调试。

// store.js
export default {debug: true,state: {user: {name: 'xiaomao',age: 18,sex: '男'}},setUserNameAction (name) {if (this.debug) {console.log('setUserNameAction triggered:', name)}this.state.user.name = name}
}
  1. 在组件中导入
<!--组件A-->
<template><div><h1>componentA</h1><!--3. 可以在视图中直接用点的方式显示数据-->user name: {{ sharedState.user.name }}<button @click="change">Change Info</button></div>
</template><script>
// 1. 在组件中导入store
import store from './store'
export default {methods: {// 4. 当点击按钮的时候,调用store的方法,将值改为componentAchange () {store.setUserNameAction('componentA')}},data () {return {// 当前组件还可以有自己的私有状态,存在privateState中privateState: {},// 2. 将store的state属性赋值给shareStatesharedState: store.state}}
}
</script><style></style>
<!--组件B,用法与上面一样,就是修改名字的时候值为componentB-->
<template><div><h1>componentB</h1>user name: {{ sharedState.user.name }}<button @click="change">Change Info</button></div>
</template><script>
import store from './store'
export default {methods: {change () {// 修改名字的时候改成了componentBstore.setUserNameAction('componentB')}},data () {return {privateState: {},sharedState: store.state}}
}
</script><style></style>

上面组件A和组件B都共享了全局的状态,并且用户都可以更改状态。调试的时候,按A组件的按钮两者都变成了componentA,点B组件的按钮两者都变成了componentB

我们不在组件中直接修改状态的值而是通过调用storeactions来修改值,这样记录的好处是: 能够记录store中所以state的变更,当可以实现记录storestate的变更时候,就可以实现高级的调试功能。例如:timeTravel(时光旅行)和历史回滚功能。

刚才使用的store,其实就类似于Vuex的仓库。

当项目比较复杂,多个组件共享状态的时候,使用组件间通信的方式比较麻烦,而且需要维护。这个时候我们可以使用集中式的状态解决方案 —— Vuex

Vuex

好的终于进入了主题~~

什么是Vuex?

  • Vuex官网
  • Vuex 是专门为 Vue.js 设计的状态管理库,从使用的角度其实就是一个JavaScript
  • 它采用集中式的方式存储需要共享的数据,如果状态特别多的话不易管理,所以Vuex还提供了一种模块的机制,按照模块管理不同的状态
  • 它的作用是进行状态管理,解决复杂组件通信,数据共享
  • Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了time-travel时光旅行、历史回滚、状态快照、导入导出等高级调试功能

什么情况下使用Vuex?

非必要的情况不要使用Vuex

Vuex 可以帮助我们管理组件间共享的状态,但是在项目中使用Vuex的话,我们需要了解Vuex中带来的新的概念和一些API,如果项目不大,并且组件间共享状态不多的情况下,这个时候使用Vuex给我们带来的益处并没有付出的时间多。此时使用简单的 store 模式 或者其他方式就能满足我们的需求。

中大型单页应用程序使用更好

中大型单页应用程序中,使用Vuex可以帮我们解决多个视图依赖同一状态来自不同视图的行为需要变更同一状态的问题。建议符合这些情况的业务,使用Vuex进行状态管理,会给我们提供更好的处理组件的状态,带来的收益会更好些。例如典型案例:购物车。

注意:不要滥用Vuex,否则会让业务变得更复杂。

Vuex核心概念回顾

下面这张图展示了Vuex的核心概念并且展示了Vuex的整个工作流程

image
  • Store:仓库,Store是使用Vuex应用程序的核心,每个应用仅有一个Store,它是一个容器,包含着应用中的大部分状态,当然我们不能直接改变Store中的状态,我们要通过提交Mutations的方式改变状态。
  • State:状态,保存在Store中,因为Store是唯一的,所以State也是唯一的,称为单一状态树,这里的状态是响应式的。
  • Getter:相当于Vuex中的计算属性,方便从一个属性派生出其他的值,它内部可以对计算的结果进行缓存,只有当依赖的状态发生改变的时候,才会重新计算。
  • Mutation:状态的变化必须要通过提交Mutation来完成。
  • Actions:与Mutation类似,不同的是可以进行异步的操作,内部改变状态的时候都需要改变Mutation
  • Module:模块,由于使用的单一状态树让所有的状态都会集中到一个比较大的对象中,应用变得很复杂的时候,Store对象就会变得相当臃肿,为了解决这些问题Vuex允许我们将Store分割成模块,每个模块拥有自己的StateMutationActionsGetter,甚至是嵌套的子模块。

Vuex基本结构

使用vue-cli创建项目的时候,如果选择了Vuex,会自动生成Vuex的基本结构。

// store.js
import Vue from 'vue'
// 导入Vuex插件
import Vuex from 'vuex'
// 通过use方法注册插件
// 插件内部把Vuex的Store注入到了Vue的实例上
Vue.use(Vuex)
// 创建了Vuex的Store对象并且导出
export default new Vuex.Store({state: {...},mutations: {...},actions: {...},modules: {...}// 如果有需要还可以有getters
})
//App.js
// 导入store对象
import store from './store'
new Vue({router,// 在初始化Vue的时候传入store选项,这个选项会被注入到Vue实例中// 我们在组件中使用的this.$store就是在这个地方注入的store,render: h => h(App)
}).$mount('#app')

State的使用

  1. 下载项目模板vuex-sample-temp ,npm install下载依赖,在store/index.js中定义两个state
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {},actions: {},modules: {}
})
  1. App.vue中使用state,然后使用npm run serve查看结果
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count}} <br/>msg: {{ $store.state.ms }}</div>
</template>
<script>
  1. 每次使用变量都要前面写 $store.state 很是麻烦,所以这里使用``Vuex内部提供的myState的函数,会帮我们生成状态对应的计算属性
<template><div id="app"><h1>Vuex - Demo</h1><!--4. 在使用的时候直接用计算属性count和msg即可-->count: {{ count }} <br/>msg: {{ msg }}</div>
</template>
<script>// 1. 引入vuex的mapState模块import { mapState } from 'vuex'export default {// 2. 在计算属性中调用mapState函数computed: {// 3. mapState需要接收数组作为参数,数组的元素是需要映射的状态属性// 会返回一个对象,包含两个对应属性计算的方法// { count: state => state.count, msg: state => state.msg }// 然后这里使用扩展运算符展开对象,完成之后我们就有了count和msg两个计算属性...mapState(['count', 'msg'])}}
</script>
  1. 上面的方法比较简洁但是如果这个组件中本身就有count或者msg属性,就会造成名称冲突,这个时候需要设置别名。
<template><div id="app"><h1>Vuex - Demo</h1><!-- 使用的时候直接使用别名即可 -->count: {{ num }} <br/>msg: {{ message }}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {computed: {// mapState可以传对象,键是别名,值是映射的状态属性...mapState({ num: 'count', message: 'msg' })}
}
</script>

Getter的使用

Vuex中的getter就相当于组件中的计算属性,如果想要对state的数据进行简单的处理在展示,可以使用getter

这里用Vuexgetter处理而不是用组件中的计算属性是因为状态本身属于Vuex,应该在其内部处理

  1. store.js中设置getters
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},// 与计算属性的写法一致getters: {reverseMsg (state) {return state.msg.split('').reverse().join('')}},mutations: {},actions: {},modules: {}
})
  1. App.vue中使用
<template><div id="app"><h1>Vuex - Demo</h1><h2>reverseMsg: {{ $store.getters.reverseMsg }}</h2><br/></div>
</template>
  1. 同样那样引用过于麻烦,那么和mapState一样,使用内部的mapGetters,也是将其映射到组件的计算属性,其用法和mapState一样,也可以为了避免冲突使用对象设置别名。
<template><div id="app"><h1>Vuex - Demo</h1><h2>reverseMsg: {{ reverseMsg }}</h2><br/></div>
</template>
<script>
// 1. 引入vuex的mapGetters模块
import { mapGetters } from 'vuex'
export default {// 2. 在计算属性中调用mapGetters函数computed: {// 3. 用法与mapState一致,这里也可以使用对象设置别名...mapGetters(['reverseMsg'])}
}
</script>

Mutation的使用

状态的修改必须提交MutationMutation必须是同步执行的。

  1. 当用户点击按钮的时候,count值进行增加,先在store.js中写Mutation函数
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {// 增加函数,接收两个参数// 第一个state状态// 第二个是payload载荷,payload是mutations的时候提交的额外参数,可以是对象,这里传递的是数字increate (state, payload) {state.count += payload}},actions: {},modules: {}
})
  1. App.vue中设置按钮,并注册事件
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Mutation</h2><!-- 给按钮注册点击事件,点击的时候调用commit提交Mutation,第一个参数是调用的方法名,第二个参数是payload,传递的数据 --><button @click="$store.commit('increate', 2)">Mutation</button></div>
</template>
  1. 点击按钮的时候,count的值每次+2
  2. 下面进行写法优化,使用map方法将当前的mutation映射到methods中,其依旧会返回一个对象,这个对象中存储的是mutation中映射的方法
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Mutation</h2><!-- 这里直接写方法,,第一个参数state不需要传递,后面传payload参数为3 --><button @click="increate(3)">Mutation</button></div>
</template>
<script>
// 1. 引入vuex的mapMutations模块
import { mapMutations } from 'vuex'
export default {// 2. methods中调用mapMutations方法methods: {...mapMutations(['increate'])}
}
</script>
Mutation的调试

运行到4之后,这时看一下devtools看一下时光旅行和历史回滚,下面是初始状态

image

点一下按钮之后就增加了一个记录,还显示了改变之后的数据

image

如果数据不对,可以进行调试。

时光旅行

然后多点几下,进行时光旅行。

image

点击按钮之后,状态就变成了之前那个状态,这个功能也是为了方便调试

image
状态回滚

这个图标就是状态回滚

image

点击之后,代码就回到了没有执行这一步的状态

image
提交改变

下面那个按钮的意思是将这次提交作为最后一次提交

image

点击之后,base State变成了那次的状态,其他的状态以这个作为起始点

image

Actions的使用

如果有异步的修改,需要使用actions,在actions中可以执行异步操作,当异步操作结束后,如果需要更改状态,还需要提交Mutation

  1. actions中添加方法increateAsync
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0,msg: 'hello vue'},mutations: {increate (state, payload) {state.count += payload}},actions: {// actions中的方法有两个参数:第一个参数是context上下文,这个对象中有state,commit,getters等成员,第二个参数是payLoadincreateAsync (context, payLoad) {setTimeout(() => {context.commit('increate', payLoad)}, 2000)}},modules: {}
})
  1. App.vue中使用dispatchactions的方法都要用这个
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Actions</h2><!--这里使用了dispatch--><button @click="$store.dispatch('increateAsync',5)">Action</button></div>
</template>
  1. 进行优化,这个时候引入mapActions
<template><div id="app"><h1>Vuex - Demo</h1>count: {{ $store.state.count }} <br/><h2>Actions</h2><button @click="increateAsync(5)">Action</button></div>
</template>
<script>
// 1. 引入vuex的mapActions模块
import { mapActions } from 'vuex'
export default {methods: {// 这个是对Actions的方法的映射,把this.increateAsync映射到this.$store.dispatch...mapActions(['increateAsync'])}
}
</script>

Modules的使用

模块可以让我们把单一状态树拆分成多个模块,每个模块都可以拥有自己的statemutationactiongetter甚至嵌套子模块。

模块定义

store文件夹中,创建一个modules文件夹,里面每一个js文件就是一个模块,下面是每一个模块的定义格式

const state = {products: [{ id: 1, title: 'iPhone 11', price: 8000 },{ id: 2, title: 'iPhone 12', price: 10000 }]
}
const getters = {}
const mutations = {setProducts (state, payload) {state.products = payload}
}
const actions = {}export default {namespaced: false,state,getters,mutations,actions
}
模块注册
  1. 先导入这个模块
import products from './modules/products'
import cart from './modules/cart'
  1. 后来在modules选项中注册,注册之后这里会把模块挂载到storestate中,这里可以通过store.state.products访问到products模块中的成员,还把的模块中的mutation成员记录到了store的内部属性_mutation中,可以通过commit直接提交mutation
export default new Vuex.Store({state: {},getters: {},mutations: {},actions: {},modules: {products,cart}
})
模块使用
  1. App.vue中使用,state就点出来,mutation还是用commit方法
<template><div id="app"><h1>Vuex - Demo</h1><h2>Modules</h2><!--第一个products是products模块,第二个products是模块的state的products属性-->products: {{ $store.state.products.products }} <br/><button @click="store.commit('setProducts',[])">Mutation</button></div>
</template>
添加命名空间

因为每个模块中的mutation是可以重名的,所以推荐使用命名空间的用法,方便管理。

  1. 在开启命名空间的时候,在模块的导出部分添加namespaced
const state = {}
const getters = {}
const mutations = {}
const actions = {}export default {// true就是开启,false或者不写就是关闭namespaced: false,state,getters,mutations,actions
}
  1. 使用的时候在App.vue中要设置state是模块中出来的,如果没有命名空间,就是全局的state的。
<template><div id="app"><h1>Vuex - Demo</h1>products: {{ products }} <br/><button @click="setProducts([])">Mutation</button></div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
export default {computed: {// 模块中的state,第一个参数写模块名称,第二个参数写数组或者对象...mapState('products', ['products'])},methods: {// 模块中的mutations,第一个写模块名称,第二个写数组或者对象...mapMutations('products', ['setProducts'])}
}
</script>

Vuex严格模式

所有的状态变更必须提交mutation,但是如果在组件中获取到$store.state.msg进行修改,语法层面没有问题,却破坏了Vuex的约定,且devTools也无法跟踪到状态的修改,开启严格模式之后,如果在组件中直接修改state,会报错。

  1. index.js,初始化Store的时候开启严格模式
export default new Vuex.Store({strict: true,state: {...},...
}
  1. App.vue中使用直接赋值的语句
<template><div id="app"><h1>Vuex - Demo</h1><h2>strict</h2><button @click="$store.state.msg = 'hello world~'">strict</button></div>
</template>
  1. 点击按钮内容改变,但是控制台会抛出错误
image

注意:不要在生产环境开启严格模式,因为严格模式会深度检测状态树,会影响性能。在开发模式中开启严格模式,在生产环境中关闭严格模式

export default new Vuex.Store({strict: process.env.NODE_ENV !== 'production',state: {...
}

Vuex插件

  • Vuex插件就是一个函数,接收一个store的参数
  • 在这个函数中可以注册函数让其在所有的mutations结束之后再执行
插件的使用
  • 插件应该在创建Store之前去创建
  • subscribe函数
    • 作用是去订阅store中的mutation
    • 他的回调函数会在每个mutation之后调用
    • subscribe会接收两个参数,第一个是mutation,还可以区分模块的命名空间,第二个参数是state,里面是存储的状态
  1. 定义插件
// 这个函数接收store参数
const myPlugin = store => {// 当store初始化后调用store.subscribe((mutation, state) => {// 每次 mutation 之后调用// mutation 的格式为 { type, payload }// type里面的格式是 "模块名/state属性"// state 的格式为 { 模块一, 模块二 }})
}
  1. Store中注册插件
const store = new Vuex.Store({//...plugins: [myPlugin]
})


喜欢的朋友记得点赞、收藏、关注哦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85602.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85602.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

操作系统---内存管理上

文章目录 1. 内存的基础知识1.1 什么是内存&#xff0c;有何作用1.2 进程运行的基本原理1.2.1 指令的工作原理1.2.2 逻辑地址 VS 物理地址 1.3 如何实现地址转换&#xff08;逻辑 -> 物理&#xff09;1.3.1 绝对装入1.3.2 可重定位装入&#xff08;静态重定位&#xff09;1.…

医学图像处理期末复习

目录 考试范围第1章 绪论1.1 数字图像处理的概念1.2 数字图像处理的应用领域1、医学领域2、其他领域 1.3 数字图像处理基础1.4 数字图像基础运算 第2章 医学图像灰度变换与空间滤波2.1 医学图像灰度变换线性灰度变换非线性灰度变换 2.2 直方图均衡化√2.3 空间平滑滤波线性空间…

类图:软件世界的“建筑蓝图”

本文来自「大千AI助手」技术实战系列&#xff0c;专注用真话讲技术&#xff0c;拒绝过度包装。 类图&#xff08;Class Diagram&#xff09;&#xff1a;软件世界的“建筑蓝图” 类图&#xff08;Class Diagram&#xff09;是统一建模语言&#xff08;UML&#xff09; 中最重要…

利用DevEco Studio对RK3588的HiHopesOS-4.1.110(OpenHarmony)进行Qt程序编写

文章目录 热身准备添加Qt库运行qml程序 热身 可以先看我这篇文章【DevEco Studio中使用Qt&#xff0c;编写HarmonyOS程序】 准备 板子的主要信息 目前由于系统版本&#xff08;API 11&#xff09;及其他原因&#xff0c;只能用4.1版本的DevEcoStudio来编写&#xff0c;更高…

设计模式精讲 Day 5:原型模式(Prototype Pattern)

【设计模式精讲 Day 5】原型模式&#xff08;Prototype Pattern&#xff09; 文章内容 在“设计模式精讲”系列的第5天&#xff0c;我们将深入讲解原型模式&#xff08;Prototype Pattern&#xff09;。作为创建型设计模式之一&#xff0c;原型模式通过复制已有对象来创建新对…

深度学习——第2章习题2-1分析为什么平方损失函数不适用于分类问题

深度学习——第2章习题2-1 《神经网络与深度学习》——邱锡鹏 2-1 分析为什么平方损失函数不适用于分类问题。 平方损失函数&#xff08;Quadratic Loss Function&#xff09;经常用在预测标签y为实数值的任务中&#xff0c;定义为 L ( y , f ( x ; θ ) ) 1 2 ( y − f (…

【Linux】运行脚本后打屏同时保存到本地

命令&#xff1a; sh run.sh 2>&1 | tee output.log sh run.sh 2>&1 | tee output_$(date "%Y%m%d%H%M").log作用&#xff1a;运行脚本&#xff0c;并同时将输出&#xff08;包括标准输出和错误输出&#xff09;显示到终端&#xff0c;并保存到文件中…

Spark 在小众日常场景中的实战应用:从小店数据到社区活动

Spark 在小众日常场景中的实战应用&#xff1a;从小店数据到社区活动​ 提起 Spark&#xff0c;大家往往会联想到大型互联网公司的数据处理、金融行业的复杂分析。但实际上&#xff0c;Spark 在许多小众、贴近生活的场景中也能大显身手。结合学习与实践&#xff0c;我探索了 S…

mysql 执行计划 explain命令 详解

explain id &#xff1a;select查询的序列号&#xff0c;包含一组数字&#xff0c;表示查询中执行select子句或操作表的顺序select_type&#xff1a;查询类型 或者是 其他操作类型table &#xff1a;正在访问哪个表partitions &#xff1a;匹配的分区type &#xff1a;访问的类…

让大模型“更懂人话”:对齐训练(RLHF DPO)全流程实战解析

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

GO 原子操作面试题及参考答案

Go 的 sync/atomic 包和 sync.Mutex 的根本区别是什么&#xff1f; Go 语言中的 sync/atomic 包和 sync.Mutex 都用于处理并发编程中的同步问题&#xff0c;但它们的实现机制、应用场景和性能特性存在根本差异。理解这些差异对于编写高效、安全的并发代码至关重要。 sync/atomi…

MATLAB 山脊图绘制全解析:从数据生成到可视化进阶

一、引言&#xff1a;当数据分布拥有「层次感」—— 山脊图的魅力​ 在数据可视化的世界里&#xff0c;我们常常需要同时展示多个分布的形态差异。传统的重叠密度图虽然能呈现整体趋势&#xff0c;但当分布数量较多时&#xff0c;曲线交叠会让画面变得杂乱。这时候&#xff0c…

跨境电商每周资讯—6.16-6.20

1. Instagram 在亚太地区逐渐超越 TikTok 在整个亚太地区&#xff0c;Instagram用户数量正逐渐超过TikTok。预计2025年日本Instagram用户数量将增至4440万&#xff0c;印度今年用户数量将增长10%&#xff0c;领跑亚太。与之形成对比的是&#xff0c;TikTok在一些国家增长速度放…

计算机网络 网络层:数据平面(一)

前一节学习了运输层依赖于网络层的主机到主机的通信服务&#xff0c;提供各种形式的进程到进程的通信。了解这种主机到主机通信服务的真实情况&#xff0c;是什么使得它工作起来的。 在本章和下一章&#xff0c;将学习网络层实际是怎样实现主机到主机的通信服务。与运输层和应用…

Suna本地部署详细教程

一、安装基础环境 # 1、创建环境 conda create -n suna python3.11.7# 2、激活虚拟环境 conda activate suna# 3、安装jupyter和ipykernel pip install jupyter ipykernel# 4、将虚拟环境添加到jupyter # python -m ipykernel install --user --namemyenv --display-name"…

LeetCode 每日一题打卡|若谷的刷题日记 3day--最长连续序列

1.最长连续序列 题目&#xff1a; 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [1…

EfficientVLA:面向视觉-语言-动作模型无训练的加速与压缩

25年6月来自上海交大、哈工大、西安交大和电子科大&#xff08;成都&#xff09;的论文“EfficientVLA: Training-Free Acceleration and Compression for Vision-Language-Action Models”。 视觉-语言-动作 (VLA) 模型&#xff0c;特别是基于扩散的架构&#xff0c;展现出具…

wireshark抓包分析TCP数据包

1、直接从TCP的三次握手开始说起 三次握手就是客户与服务器建立连接的过程 客户向服务器发送SYN(SEQ=x)报文,然后就会进入SYN_SEND状态服务器收到SYN报文之后,回应一个SYN(SEQ=y)ACK(ACK=x+1)报文,然后就会进入SYN_RECV状态客户收到服务器的SYN报文,回应一个ACK(AC…

同等学力申硕-计算机统考-历年真题和备考经验

同等学力申请硕士学位考试是比较适合在职人员的提升学位方式&#xff0c;了解过的人应该都知道&#xff0c;现在社会的竞争压力越来越大&#xff0c;为了提高职业生存能力&#xff0c;提升学位在所难免。 一、已有计算机统考历年真题资料 报名过同等学力申硕计算机专业的朋友都…

OSI网络通信模型详解

OSI 模型就是把这整个过程拆解成了 7 个明确分工的步骤&#xff0c;每一层只负责自己那一摊事儿&#xff0c;这样整个系统才能顺畅运转&#xff0c;出了问题也容易找到“锅”在谁那。 核心比喻&#xff1a;寄快递 &#x1f4e6; 想象你要把一份重要的礼物&#xff08;你的数据…