Vuex是什么?

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

什么是“状态管理模式 ”

让我们从一个简单的计数器开始

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})
这个状态包含一下几个部分
  • state,驱动应用的数据源

  • view,以声明方式将state映射到视图上

  • actions,响应在view上用户输入导致的状态变化

    img

但是当我们遇到多个组件共享状态是,单项数据的简洁性很容易被破坏

  • 多个视图依赖同一个状态
  • 来自不同视图的行为需要变更同一状态

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

适合场景

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要的对短期和长期效益进行权衡

如果你不打算开发大型单页面应用使用Vuex可能是繁琐冗余的,如果构建中大型单页面应用,Vuex将会成为自然而然的选择

安装

使用脚手架创建vue项目后下载vuex

npm install vuex --save
创建store文件夹并在其创建index.js文件,引入vue和vuex
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
 

特点

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

  1. Vuex和状态存储是响应式的。当Vue组件从store中读取状态时,若store中的状态发生改变,那么响应的组件也会得到高效的更新
  2. 你不能直接改变store中的状态。改变store中的状态的唯一途径就是显式的提交mutation

使用store的计数器

安装Vuex后,我们需要创建你一个store。仅需要提供一个初始state对象和一些mutation

stored

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state:{
    count:10
  },
  mutations:{
    add(state){
     state.count++
    },
    sub(state){
     state.count--
    }
  }
})

export default store
为了在 Vue 组件中访问 this.$store property,你需要为 Vue 实例提供创建好的 store。Vuex 提供了一个从根组件向所有子组件,以 store 选项的方式“注入”该 store 的机制:
new Vue({
  el: '#app',
  store: store,
})
可以通过store.state来获取状态对象,以及通过store.commit方法触发状态的改变

子组件

<template>
  <div>
    <div>{{ $store.state.count }}</div>
    <button @click="sub">减少</button>
  </div>
</template>
<script>
export default {
  methods: {
    sub () {
      this.$store.commit('sub')
    }
  }
}
</script>
父组件

<template>
  <div>
    <div>{{ $store.state.count }}</div>
    <button @click="add">增加</button>
  </div>
</template>
<script>
export default {

  methods: {
    add () {
      this.$store.commit("add")
    }
  }
}
</script>
 
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <addcount />
    <subcount />
  </div>
</template>

<script>
import addcount from './components/add.vue'
import subcount from './components/sub'
export default {
  name: 'App',
  components: {
    addcount,
    subcount
  }
}
</script>
深入

1、state

单一状态树

在Vue组件中获得Vuex的状态

由于Vuex的状态是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态

<template>
  <div id="app">
    <div>姓名:{{ name }}</div>
    <div>性别:{{ gender }}</div>
    <div>作品:{{ works }}</div>
  </div>
</template>
<script>
export default {
  computed: {
    name () {
      return this.$store.state.name
    },
    gender () {
      return this.$store.state.gender
    },
    works () {
      return this.$store.state.works
    }
  }
}
</script>

mapState辅助函数

当一个组件需要获得多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用mapState辅助函数帮助我们生成计算属性

import { mapState } from 'vuex'
export default {
  computed: mapState({
    name (state) {
      return state.name
    },
    gender (state) {
      return state.gender
    },
    works (state) {
      return state.works
    }
  })
 

改为以上代码后还是显得有些冗余,所以让我们使用箭头函数来改造一下

import { mapState } from 'vuex'
export default {
  computed: mapState({
    name: state => state.name,
    gender: state => state.gender,
    works: state => state.works,
  })
}

当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。

computed: mapState([
    'name',
    'gender',
    'works'
  ])

对象展开运算符

mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符 ,我们可以极大地简化写法:

export default {
  computed: {
    localComputed(){
      //........
    },
    ...mapState(['name', 'gender', 'works'])
  }
}
效果:

在这里插入图片描述

2、getter

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数:

const store = new Vuex.Store({
  state: {
    starData: [
      { id: 1, name: '周杰伦',gender:'男' },
      { id: 2, name: '林俊杰',gender:'男' },
      { id: 3, name: '王力宏',gender:'男' },
      { id: 4, name: '范冰冰',gender:'女' },
      { id: 6, name: '林青霞',gender:'女' },
    ]
  },
  getters: {
    starCount: state => {
      return state.starData.length
    }
  }
 
<template>
  <div id="app">
    <div>一共有{{ $store.getters.starCount }}位明星</div>
  </div>
</template>
<!--页面显示:一共有5位明星--!>
————————————————