关于Vuex,你需要了解什么?

396 阅读4分钟

vuex

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

什么状态时需要在多个组件间共享?

  • 大型项目中,需要在多个界面间的共享的状态
  • 如用户的登录状态、用户名称、头像、地理位置信息等
  • 如商品的收藏、购物车中的物品等

一般父传子可以用props,但如何两个组件之间没有直接相关联的关系,就需要引入vuex

// 1 安装
npm install vuex --save

// 2.1 引入(一般main.js里引入,需要创建一个/scr/store文件夹,并在内新建一个index.js文件后引入)
import Vuex from 'vuex'
// 2.2 安装
Vue.use(Vuex);
// 2.3 创建对象
const store = new Vuex.Store({ })
// 2.4 导出storer对象
export default store

// 3 回到main.js中引入
import store from './store'

简单使用

// 在vuex对象里,新建一个state属性,用于存放公共数据
const store = new Vuex.Store({
	state: {
		xmsg: 'a share msg.'
	}
})
// 在组件模板中
<h1>{{$store.state.xmsg}}</h1>
// 绑定事件时,可以通过此种方式操作,但官方不推荐
<button @click='$store.state.xmsg'>按钮</button>

因为如果绑定到某个单一页面中时,就可能很难追踪到到底是哪个页面发生了操作,因此官方推荐以下方法,通过 Actions-->Mutations->State 的流程,并在Mutations中帮你记录发生操作的页面对象(可以在Vue Devtools工具中查看),更方便调试。

其实可以直接跳过Actions,通过 Mutations->State 的流程来实现,但这只仅限于同步操作,如果存在异步操作,就不能跳过。

故Actions主要用于异步操作,即发送网络请求时,接收后端数据(Backend API)。

state

存放公共状态/共享数据

vue提出一个单一状态树(Single Source of Truth)的概念,即推荐你的状态信息只保存到单一的一个Store对象中,如果创建多个Store对象,那么之后的管理和维护等都会变得特别困难。所以Vuex使用了单一状态树来管理应用层级的全部状态,它能够以最直接的方式找到某个状态的片段,并在之后的维护和调试过程中,方便管理和维护。

改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation

getters

类似于计算属性,存放公共的计算属性,也可用作过滤和传参

如果我们已经有了一个获取所有年龄大于20岁学生列表的getters,那么代码可以这样来写

getters:{
	greaterAgesstus: state => {
		return state.students.filter(s => s.age >= 20)
},
	greaterAgescount: (state, getters) => {
		return getters.greaterAgesstus.length
}
}

getters 默认不能传递参数,如果希望传递参数,那么只能让getters本身返回另一个函数。

比如上面的案例中,我们希望根据ID获取用户的信息

getters:{
	stuByID: state => {
 return id => {
   return state.students.find(s => s.id === id)
 }
}
}

mutations

状态更新,用于存放公共操作的函数

  • ==Vuex 的 store 状态更新的唯一方式是提交 Mutation==
  • Mutation 主要包含两部分:字符串的事件类型(type)和一个回调函数(handler),该回调函数的第一个参数自动默认传入一个state参数,第二个参数的载荷(payload),在更新数据时顺带传入额外的参数,payload也可以是一个对象
// 计数器案例
<h1>{{$store.state.count}}</h1>
<button @click='add'>+</button>
<button @click='sub'>-</button>
<button @click='addNum(10)'>+10</button>
methods:{
  add(){ this.$store.commit('increment') },
  sub(){ this.$store.commit('decrement') },
  addNum(num){ this.$store.commit('incrementNum', num) },
 
  // 特殊的提交封装 (使用payload传入多个对象参数时)
  addNum(num){ 
    this.$store.commit({
      type: 'incrementNum2', 
      num
    }),
  }
}
// 通过vuex的commit方法来间接操作
const store = new Vuex.Store({
	state: {
		count: 0
	},
  mutations: {
    increment(state) {
      state.count++
    },
    decrement(state) {
      state.count--
    },
 		incrementNum(state, num) {
      state.count += count
    },
    incrementNum2(state, payload) {
      state.count += payload.count
    }
  }
})

Mutations d响应原则:1.必须提前在store中初始化所需的属性;2.当需要做响应式操作时,通过 set()delete() 方法

mutations: {
	updateInfo(state) {
    // 不能用这种方法 无法实现响应式
    state.info['address'] = 'beijing';
    delete state.info.msg;
    
    // 只能通过以下方法
    Vue.set(state.info, 'address', 'beijing');
    Vue.delete(state.info, 'msg');
  }
}

通常情况下,Vuex要求 Mutation中定义的方法必须是同步的,主要的原因是当我们使用devtools时,可以devtools可以帮助我们捕捉mutation的快照;但如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候会被完成。

要实现异步操作,必须使用 actions

在vuex中的mutations方法,想调用mutations中的另外一方法,还是用commit,只需要使用this.commit('function'),当前的this指向的就是当前模块中的mutations;

传多个参数的写法:

此时参数不能继续在后面加,后面的参数无效,传进去的参数为undefined;

官网的解释:In most cases, the payload should be an object so that it can contain multiple fields, and the recorded mutation will also be more descriptive;

所以,我们可以将参数以对象的方式传进去,多个属性就是多个参数了。

blog.csdn.net/weixin_3056…

actions

默认传入一个参数是 context

mutations: {
  updateInfo(state) {
    state.info.msg = 'hello';
}
actions: {
	aUpdateInfo(context) {
    // 模拟异步操作
    setTimeout(() => {
      // 不能这样操作 state只能在mutations里实现
      // context.state.info.msg = 'hello' 
      context.commit(updateInfo)
    }, 1000)
  }
}
metheds: {
  updateInfo() {
    this.$store.dispatch('aUpdateInfo')
  }
}

modules

Module是模块的意思,为什么在Vuex中我们要使用模块呢

Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理。 当应用变得非常复杂时,store对象就有可能变得相当腌肿 为了解决这个问题,Vuex允许我们将store分割成模块(Module),而每个模块拥有自己的state、mutations、actions、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 的状态

store 目录结构

/store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    ├── getters.js				# 根级别的 getters
    └── /modules
        ├── modulesA.js   # 模块A
        └── modulesB.js   # 模块B
import Vue from 'vue'
import Vuex from 'vuex'

import mutations from './mutations
import actions from './actions
import getters from './getters
import moduleA from './modules/modulesA
import moduleB from './modules/modulesB


Vue.use(Vuex);

const state = {
  ...// state 无需单独导入
}
  
const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  
  modules: {
  	a: moduleA,
  	b: moduleB
	}
})

export default store
// mutations.js actions.js getters.js 单独以文件形式存放
export default {
  ...
}