认识Vuex
背景
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
Vuex是一个提供在多个组件间共享状态的插件,且状态是响应式的。
单页面状态管理
State:状态。(可以当做就是data中的属性)
View:视图层,可以针对State的变化,显示不同的信息。
Actions:主要是用户的各种操作:点击、输入等等,会导致状态的改变。
<template>
<div class="test">
<div>当前计数: {{counter}}</div>
<button @cli ck="counter+=1"> +1</button>
<button @click="counter-=1">-1</button>
</div>
</template>
<script>
export default {
name: 'Helloworld',
data () {
return {
counter: 0
}
}
}
</script>
多页面状态管理(vuex)
不同界面的Actions都想修改同一个状态,现在要做的就是将共享的状态抽取出来,交给vuex。之后,每个组件,按照规定好的规定,进行访问和修改等操作。
Vuex状态管理图例
Vue components可以直接到mutations(但是不建议这样做)。Mutations中的操作都是同步操作,要是有异步操作就在actions中操作,操作完成之后再提交到mutations中进行同步操作。Devtools跟踪不到异步操作。
Devtools是Vue开发的一个浏览器插件,可以帮助记录每次修改state的记录
Vuex基本使用
Vuex核心概念
State
Vuex使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。state
是响应式的, 当state
中的数据发生改变时, Vue组件会自动更新。
Getters
类似于组件中的计算属性。当数据必须经过一系列变化之后再在页面上使用时就用到计算属性。
同理,如果是在state
中的数据,需要进行操作后在页面显示,就用到getters
属性。
const store = new Vuex . Store({
state: {
students: [
{id: 110,name:'why',age:18},
{id: 111,name:'kobe',age:21},
{id: 112,name:'lucy',age:25},
{id: 113,name:'lilei',age:30},
]
},
getters: {
//获取学生年龄大于20的个数。
greateragesCount: state =>{
return state. students. filter(s => s.age >= 20) . length
}
}
})
Getters作为参数和传递参数
如果我们已经有了一个获取所有年龄大于20岁学生列表的getters, 那么代码可以这样来写
getters: {
greaterAgesstus: state => {
return state.students.filter(s => s.age >= 20)
},
greateragesCount: (state, getters) => {
return getters.greaterAgesstus.length
}
}
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
Mutation
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:字符串的事件类型(type);一个回调函数(handler),该回调函数的第一个参数就是state。
Mutation基本使用
mutation的定义方式:
mutations: {
increment(state) {
state.count++
}
}
通过mutation更新
increment:function() {
this.$store.commit('increment')
}
Mutation传递参数
在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数。参数被称为是mutation的载荷(Payload)
当参数只有一个的时候,可以在mutation中使用参数
decrement(state,n) {
state.count -= n;
}
decrement:function() {
this.$store.commit('decrement',2);
}
如果参数不是一个,通常会以对象的形式传递, 也就是payload
是一个对象。再从对象中取出相关的信息。
changeCount(state.pay1oad) {
state.count = payload.count;
}
changeCount:function () {
this.$store.commit('changeCount',{count: 0});
}
Mutation提交风格
Vue还提供了另外一种风格, 它是一个包含type属性的对象
this.$store.commit({
type: 'changeCount',
count: 100
})
Mutation中的处理方式是将整个commit的对象作为payload
使用, 所以代码没有改变, 依然如下:
changeCount(state,pay1oad) {
state.count = pay1oad.count
}
Mutation响应规则
提前在state中初始好的属性是响应式的。之后若要修改(添加)state中的数据是其为响应式。有两种方式:
方式一: 使用Vue.set(obj, 'newProp', 123);
方式二: 用新对象给旧对象重新赋值
App.vue
<template>
<div id="app">
<p>我的个人信息: {{info}}</p>
<button @click="updateInfo">更新信息</button>
</div>
</template>
<script>
export default {
name:'App',
components: {},
computed: {
info() {
return this.$store.state.info;
}
},
methods: {
updateInfo() {
this.$store.commit('updateInfo', {height: 1.88})
}
}
}
</script>
store-->index.js
const store = new Vuex.Store({
state: {
info:{
name:'why',age: 18
}
},
mutations: {
updateInfo(state, payload) {
//在学习教程中说这样修改不是响应式的,但是实践后是响应式的。
state.info['height'] = payload.height;
//方式一: Vue.set()
Vue.set(state.info, 'height', payload.height);
//方式二:给info赋值一个新的对象
state.info = {...state.info, 'height': payload.height}
}
}
});
Mutation常量类型
- 背景
当项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多。方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况。
一种很常见的解决方案就是使用常量替代Mutation事件的类型。可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.
- 具体操作
可以创建一个文件: mutation-types.js
, 并且在其中定义常量。定义常量时, 可以使用ES2015
中的风格, 使用一个常量来作为函数的名称。
mutation-types.js
export const UPDATE_INFO = 'UPDATE_INFO';
store-->index.js
import Vuex from 'vuex'
import Vue from 'vue'
import * as types from './mutation-types'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
info: {
name:'why',age:18
}
},
mutations:{
[types.UPDATE_INFO](state, payload) {
state.info = {...state.info, 'height': payload.height}
}
}
});
App.vue
<script>
import {UPDATE_INFO} from "./store/mutation-types";
export default {
name:App',
components: {},
computed: {
info() {
return this.$store.state.info;
}
},
methods: {
updateInfo() {
this.$store.commit(UPDATE_INFO, {height: 1.88});
}
}
}
</script>
Mutation同步函数
Vuex要求Mutation
中的方法必须是同步方法。原因是当使用devtools时, devtools可以帮助我们捕捉mutation的快照。如果是异步操作, devtools不能很好的追踪这个操作什么时候会被完成。通常情况下, 不要再mutation
中进行异步的操作。
Action
Action基本定义
Action
类似于Mutation
, 但是是用来代替Mutation
进行异步操作的.
Action的基本使用代码如下:
const store = new Vuex.Store({
state:{
count: 0
},
mutations:{
increment(state) {
state.count++;
}
}
actions: {
increment(context) {
context.commit('increment');
}
}
});
context
是和store
对象具有相同方法和属性的对象.可以通过context
去进行commit
相关的操作, 也可以获取context.state
等。定义了actions
, 如果在Vuex中有异步操作, 那么我们就可以在actions
中完成了.
Action的分发
如果调用action
中的方法, 那么就需要使用dispatch
。它也支持传递payload
。
//App.vue
methods:{
increment() {
this.$store.dispatch('increment',{cCount: 5});
}
}
//store-->index.js
mutations: {
increment(state, payload) {
state.count += payload.cCount;
},
actions: {
increment(context, payload) {
setTimeout(() => {
context.commit('increment', payload)
},5000);
}
}
Action返回的Promise
在Action
中, 我们可以将异步操作放在一个Promise
中, 并且在成功或者失败后, 调用对应的resolve
或reject
。
store-->index.js
actions: {
increment(context) {
return new Promise((resolve) => {
setTimeout(() => {
context.commit('increment');
resolve()
},1000);
})
}
}
App.vue
methods: {
increment() {
this.$store.dispatch('increment').then(res => {
console.log('完成了更新操作');
})
}
}
Module
认识Vuex的Module
Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理。当应用变得非常复杂时,store
对象就有可能变得相当臃肿。为了解决这个问题, Vuex允许将store
分割成模块(Module
), 而每个模块拥有自己的state
、mutation
、action
、getters
等。
const moduleA = {
state:{ ... },
mutations:{ ... },
actions:{ ... },
getters: { ... }
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.store({
modules:{
a:moduleA,
b:moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
Module的局部状态
mutation和getters接收的第一个参数是局部状态对象。
store-->index.js
const module = (
state:{
count:0
},
mutations:{
increment(state) {
state.count++;
},
}
getters: {
doubleCount(state) {
return state.count*2;
}
}
}
const store = new Vuex.store({
state:{
gCount:111
},
modules:{
a:moduleA,
b:moduleB
}
})
App.vue
<script>
export default {
name:'App',
components: {},
computed: {
count() {
return this.$store.getters.doubleCount
}
},
methods: {
increment() {
this.$store.commit('increment');
}
}
}
</script>
注意:虽然, 我们的doubleCount
和increment
都是定义在对象内部的。但是在调用的时候, 依然是通过this.$store
来直接调用的
Module的Actions写法
局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
。
const moduleA = {
actions: {
incrementIfoddonRootSum ( {state, commit,rootState} ) { //context 对象的解构
if ((state.count + rootState.count) % 2 === 1) {
commit('increment');
}
}
}
项目组织结构
官方建议:state不抽离。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。