vuex入门

732

vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。 通俗的解释就是,Vuex 就是为 vue 组件之间进行数据共享而开发的插件。

vuex 什么时候用?

实现数据通信的方式

对于父子组件,可以通过 props 实现数据通信; 不同非父子组件可以引入 vue 对象通过 emit/on 实现; 也可以通过不大优雅的 localStorage 存储;

vuex 是另一种解决方案,如果不同组件有大量数据需要共享通信,vuex 会更适合。

开始

首先搭建一个 vue 项目,安装 vuex。

NPM

npm n install vuex --save

需要注意的是这里一定要加上 –save,因为你这个包我们在生产环境中是要使用的。

Yarn

yarn add vuex

直接下载 / CDN 引用

unpkg.com/vuex 下载到本地,引入

<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>

并不推荐用 script 方式引入

在项目中新建一个 vuex 文件夹,并在文件夹下新建 store.js 文件,文件中我们的 vue 和vuex。

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

使用 vuex,引入之后用 Vue.use 进行引用。

Vue.use(Vuex);

初尝试 demo

1.在 store.js 文件里增加一个常量对象。

const state = {
    count:1
}

2.用 export default 输出对象,让外部可以引用。

export default new Vuex.Store({
    state
})

3.新建一个 vue 组件,在 components 文件夹下,命名 counter.vue。 在模板中引入们刚建的 store.js 文件,并在模板中用 {{ $store.state.count }} 输出 count 的值。

<template>
    <div>
        <h2>{{ msg }}</h2>
        <hr/>
        <h3>{{ $store.state.count }}</h3>
    </div>
</template>
<script>
    import store from '@/vuex/store'
    export default{
        data(){
            return{
                msg:'Hello Vuex',
            }
        },
        store
    }
</script>

4.在 store.js 文件中加入两个改变 state 的方法。

const mutations={
    add(state){
        state.count++;
    },
    reduce(state){
        state.count--;
    }
}
export default new Vuex.Store({
	state,
	mutations
})

5.在 counter.vue 模板中加入两个按钮,并调用 mutations 中的方法。

<div>
    <button @click="store.commit('add')">+</button>
    <button @click="store.commit('reduce')">-</button>
</div>

点击按钮可以看到count数字的变化。

访问state数据

1.通过 computed 计算属性获取到 count

computed: {
    count () {
      return this.$store.state.count
    }
}

或者

computed: {
    count () {
      return store.state.count
    }
}

在模板里直接访问 {{ count }} 变能获取到, store 即挂载在vue实例属性上的 store 对象
通过 this.store 可以访问 store 实例后的属性和方法

2.通过 mapState 辅助函数获取到 count

import { mapState } from 'vuex'
 computed: mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,
  })

我们也可以给 mapState 传一个字符串数组。

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

mapState 可以接受函数也可以接受一个数组,最后都统一返回一个 key value 形式的对象,使用数组意味着 key 和 value 名称是一样的。 当你需要获取多个数据时,mapState 可使代码更简练。

3.对象展开运算符

如果 computed 需要写多个对象,展开运算符会是一个很棒的选择。

computed:{
    ...mapState(["count"]),
    ...mapGetters(["count"])
}

Mutations 修改状态

状态可以理解为 state 数据,我们不能直接改变 this.$store.state 里的数据,需要调用 vuex 本身封装出来的方法,而这也恰好体现了单一数据流,

$store.commit( )

Vuex 提供了 commit 方法来修改状态,我们看一下在 demo 中所写的。

<button @click="$store.commit('add')">+</button>
<button @click="$store.commit('reduce')">-</button>
const mutations={
    add(state){
        state.count++;
    },
    reduce(state){
        state.count--;
    }
}

传值(payload)

mutations的方法,第一个默认就是 state,第二个参数才是你所传递的

const mutations={
    add(state,n){
        state.count+=n;
    }
}
 <button @click="$store.commit('add',10)">+</button>

除了字符串数字类型,还可以传递对象装载更多数据

mutations: {
  add (state, payload) {
    state.count += payload.amount
  }
}
store.commit(add, {
  amount: 10
})
// 也可以
store.commit({
  type: 'add',
  amount: 10
})

获取 Mutations方法

import { mapState,mapMutations } from 'vuex';
methods:mapMutations([
    'add','reduce'
])
// 也可以
methods:mapMutations({
  add: 'add',
  reduce: 'reduce'
})

methods 还可以

methods: {
    ...mapMutations([
        'add','reduce'
    ]),
    other () {
        
    }
}
<button @click="add">+</button>

推荐使用数组方式,更为简单方便

getters 计算过滤

在 store.js 里用 const 声明我们的 getters 对象

const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

把写好的 getters 传给 Vuex.Store 对象,输出

export default new Vuex.Store({
    state,
    mutations,
    getters
})

在组件里应用

import {mapState, mapMutations, mapGetters} from 'vuex';
computed:{
    ...mapState(["count"]),
    ...mapGetters(["evenOrOdd"]),
},
<p>{{ evenOrOdd }}</p>

便能获取到 evenOrOdd 的值

actions 异步修改状态

Action 类似于 mutation,不同点是,Action 提交的是 mutation,actions 是异步的改变状态,而 Mutations 是同步改变状态。 注册一个简单的 actions:

const actions ={
    addAction(context){
        context.commit('add',10)
    },
    reduceAction({commit}){
        commit('reduce')
    }
}
export default new Vuex.Store({
	state,
	mutations,
	getters,
	actions
})

context是上下文对象,也就是就是 store 对象 通过参数解构可以直接拿到commit 和mutation类似 直接调用 mutation 方法是 commit 调用,Action方法是使用 dispatch 也可以使用辅助函数

methods:{
    ...mapMutations(['add','reduce']),
    ...mapActions(['addAction','reduceAction'])
},
 <button @click="addAction">+</button>
 <button @click="this.$store.dispatch('reduceAction')">-</button>

Actions 支持同样的载荷方式和对象方式进行分发:

// 以载荷形式分发
store.dispatch('addAction', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'addAction',
  amount: 10
})

actions 本身是用 promise 封装实现,dispatch 依然返回 promise 所以你可以

store.dispatch('actionA').then(() => {
  // ...
})

也可以

actions: {
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  } 
}

actions 往往用于结合 axios 实现异步请求数据

Module

大型项目多人开发大量的数据共享时,建议使用 Module

之前写法

export default new Vuex.Store({
	state,
	mutations,
	getters,
	actions
})

Module 写法

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

调用从 store.state.count 应写成

store.state.a.count

若共享数据很多,可以给数据分组。

总结

state 用于数据存储

Getter 相当于 state 的计算属性

Mutation 是改变 state 的方法

Action 是异步改变,提交给 Mutation

分别对应辅助函数 mapState,mapGetter,mapMutation,mapAction

commit 调用 Mutation 方法

dispatch 是调用 Action 方法

最后看 vuex 流程图,是不是大彻大悟了?

image

Vuex 思想理念

vuex 使用单一状态树——用一个对象就包含了全部的应用层级状态。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

每一个 Vuex 应用的核心就是 store(仓库)。store 基本上就是一个容器,它包含着应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新

  2、不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解应用 Vuex 背后的基本思想,借鉴了 Flux、Redux 和 The Elm Architecture