Vuex入门

202 阅读5分钟

官方介绍

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

有点绕口,个人认为

Vuex是为了方便数据的操作而建立的一个数据操作仓库,简称:存放数据读写工具的仓库

Vuex 核心概念

store     仓库
State     单一状态树(data)
Getter    计算属性
Mutation  方法
Action    调用方法(异步)
Module    模块

下面代码是一个vue简单的计数应用

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

这个状态自管理的应用包含以下几个部分:
state    驱动应用的数据源;
view     以声明方式将 state 映射到视图
actions  响应在 view 上的用户输入导致的状态变化

上面代码是一个单向数据流的简单计数应用,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏

多个视图依赖于同一状态
传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力

来自不同视图的行为需要变更同一状态
我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝

因此,我们把组件的共享状态抽取出来,以一个全局单例模式管理,在这种模式下
我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为

Vuex 的核心是store(仓库)

store是一个容器,它包含着你的应用中大部分的状态 (state)

Vuex和单纯的全局对象有两点不同
(1)Vuex的状态存储是响应式的
当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新
(2)你不能直接改变 store 中的状态
改变store中的状态的唯一途径就是显式地提交(commit)mutation,这样使得我们可以方便地跟踪每一个状态的变化

创建一个简单的store,提供一个初始state对象和一些mutation

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

通过store.state来获取状态对象,以及通过store.commit方法触发状态变更

console.log(store.state.count)     // 1
store.commit('increment')

在根组件中为vue实例提供store,任意组件就可以使用store中的state和mutations

new Vue({
  el: '#app',
  store
})

state(单一状态树)

用一个对象包含了全部的应用层级状态(包含了项目里的所有组件的数据)
单一状态树让我们能够直接地定位任一特定的状态片段(这个全局对象,让我们在全局随意的使用数据)
存储在Vuex中的数据和Vue实例中的data遵循相同的规则,例如状态对象必须是纯粹 (plain) 的

在Vue组件中获得Vuex状态

Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态
通过在根实例中注册store选项,store实例会注入到根组件下的所有子组件中,子组件能通过 this.$store访问到

// 创建一个 Counter 组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

Getter(可以认为是 store 的计算属性)

getter就像计算属性一样,getter的返回值会根据它的依赖被缓存起来,当它的依赖值发生了改变会被重新计算

Getter接受state作为其第一个参数

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Getter会成为store.getters对象,通过属性的形式访问这些值

store.getters.doneTodos        //[{ id: 1, text: '...', done: true }]

Getter可以接受其他getter作为第二个参数

getters: {
  doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

store.getters.doneTodosCount    //1

在任意组件中使用Getter

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

getter可以返回一个函数,来给getter传参,例如对store里的数组进行查询

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

store.getters.getTodoById(2)    //{ id: 2, text: '...', done: false }
getter在返回函数时,每次都会去进行调用,而不会缓存结果

Mutation(相当于methods方法)

状态修改(增删改查),Mutation必须是同步函数,调用函数,就会修改改数据

接受state作为第一个参数

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

调用mutation

store.commit('increment')

向store.commit传参数:payload(载荷)

mutations: {
  increment (state, n) {
    state.count += n
  }
}

store.commit('increment', 10)

payload可以是一个对象,包含多个字段

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

store.commit('increment', {
  amount: 10
})

使用对象方式调用mutation

store.commit({
  type: 'increment',
  amount: 10
})

Action(调用mutation)

Action类似于mutation,但提交的是mutation,而不是直接变更状态,Action包含任意异步操作

实现简单的Action

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action函数接受一个与store实例具有相同方法和属性的context对象
调用context.commit提交mutation,或者通过context.state和context.getters来获取 state 和 getters

解构写法

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

调用 Action

store.dispatch('increment')

传参和对象方式进行调用

// 传参
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式调用
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

在action中执行异步操作

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

在组件中调用 action

在组件中使用 this.$store.dispatch('xxx') 调用action

使用mapActions辅助函数将组件的methods映射为store.dispatch调用

Module(模块)

因为Vuex使用单一状态树,应用的所有状态会集中到一个对象,当应用变得非常复杂时,store会变得相当臃肿
可以将store分割成模块,每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块

分割模块

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 的状态

模块内部的mutation和getter,接收的第一个参数是模块的局部状态对象

const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

模块内部的getter,会作为根节点状态的第三个参数

const moduleA = {
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

Vuex总结

Vuex是通过全局注入store对象,来实现组件间的状态共享

在大型复杂的项目中(多级组件嵌套),需要实现一个组件更改某个数据
多个组件自动获取更改后的数据进行业务逻辑处理,这时候使用vuex比较合适

假如只是多个组件间传递数据,使用vuex未免有点大材小用,其实只用使用组件间常用的通信方法即可