Vuex

Vuex是做什么的

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能

那么状态管理到底是什么

  • 状态管理模式、集中式存储管理
  • 可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面
  • 然后将这个对象放在顶层的Vue实例中,让其他组件可以使用
  • 那么多个组件是不是就可以共享这个对象中的所有变量属性了呢

我要管理什么状态呢
如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
比如用户的登录状态、用户名称、头像、地理位置信息等等。
比如商品的收藏、购物车中的物品等等。
这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的

经过上面的讨论我们有必要引入一个可以全局管理信息的工具也就是我们的Vuex

Vuex状态管理

单界面的状态管理

State:状态。(你姑且可以当做就是data中的属性)
View:视图层,可以针对State的变化,显示不同的信息。(这个好理解吧?)
Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变

三者是相互转换的。

<template>
  <div id="app">

    <h2>{{message}}</h2>
    <div>当前计数:{{counter}}</div>
    <button @click="counter++">+1</button>
    <button @click="counter--">-1</button>

  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
    return{
      message: '你好我是APP组件',
      counter: 0
    }
  }
}
</script>
<style>
</style>

是不是感觉和以往直接写差不多,确实,而Vuex主要是在多页面有着更加方便的运用

多页面的状态管理

vuex 挂载

在src目录下创建store /index.js

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

Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
    counter: 1000
  },
  mutations: {
    inc(state){
      state.counter++;
    },
    dec(state){
      state.counter--;
    }
  }
})
export default store;
<template>
  <div id="app">

    <h2>{{message}}</h2> 
    <div>当前计数:{{$store.state.counter}}</div>  // 使用公共的Vuex中属性
    <button @click="add">+1</button>
    <button @click="sub">-1</button>

    <hello-vux></hello-vux>
  </div>
</template>
<script>
import HelloVux from "./components/helloVux";
export default {
  name: 'App',
  components: {HelloVux},
  data(){
    return{
      message: '你好我是APP组件',
      counter: 0
    }
  },
  methods: {
    add(){
      this.$store.commit('inc')  //提交,使用commit 传入VUex 中注册的方法
    },
    sub(){
      this.$store.commit('dec')
    }
  }
}
</script>
<style>
</style>

Vuex核心

State 单一状态树

单一store管理应用层级的所有状态

Getters

类似于计算属性

经过变化后在反馈

getter 中可以传入store的 state和getter ,但是外部传入参数的时候,可以返回一个函数,在函数中设置参数

getters:{
  getStudent(state){
    return state.students.filter(s=>s.age>20)
  },
  getStudentlength(state,getters){
    return getters.getStudent.length
  },
  moreAgestu(state){
    return function (age) {
      return state.students.filter(s => s.age>age);
    }
  }
},

组件中带参调用的方法

<h2>{{$store.getters.moreAgestu(20)}}</h2>

Mutation

事件类型(type)

回调函数

通过mutation 更新数据的时候,可以携带一些参数

参数称为mutation的载荷(payload)

<button @click="add1(5)">+5</button>
<button @click="addstu">添加学生</button>
add1(count){
  this.$store.commit('inc1',count)
},
addstu(){
  const stu = {id: 12, name:'wee',age: 56};
  this.$store.commit('addstu',stu);

}
inc1(state,count){
  state.counter += count;
},
addstu(state,stu){
  state.students.push(stu);
}

提交风格

通过type 这种风格提交,在vuex 中接收的时候是以载荷(payload),是一个对象

add1(count){
  // this.$store.commit('inc1',count)
  this.$store.commit({
    type: 'inc1',
    count
  })
},
inc1(state,payload){
  state.counter += payload.count;

  },

响应规则

提前在state中定义,这些属性都会被加入到响应系统中,响应式系统会监听属性的变化,当属性变化时候回通知所有用到该属性所在的页面

splice 和vue.set()是响应式的

在mutation里面使用异步操作,devtools(一个浏览器插件)无法监听

Vue.set(state.students, 'saddress','洛杉矶' )
Vue.delect(statue.students,'age')

Mutation常量类型 – 概念

考虑下面的问题

在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
如何避免上述的问题
在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.
我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.

具体怎么做
我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.

Actions

不要再Mutation中进行异步操作.
Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.

Actions基本操作

const store = new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        addCount (state) {
            state.count++
        }
    },
    actions: {
        // 默认参数 context 上下文
        // 所用数据修改都要在mutations中, 所以在actions中异步后, 也要去mutations中修改 
        // 在mutations中修改, 才能被devtools监控到
        addCount (context) {
            context.commit('addCount');
        }
    }
})
methods: {
    addCount () {
        // 调用actions中的方法, 使用的是 dispatch
        this.$store.dispatch('addCount');
    })
}

Action返回的Promise

actions: {
    addCount (context) {
        return new Promise((resolve) => {
            setTimout (() => {
                context.commit('addCount');
                resolve();
            }, 1000)
        })
    }
}
methods: {
    addCount () {
        this.$store.dispatch('addCount').then(res => {
            console.log('完成了接口的操作')
        })
    }
}

Module

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

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

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

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

store.state.a // mouduleA的state的状态
store.state.b // mouduleB的state的状态

Module局部状态

上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写.
我们在moduleA中添加state、mutations、getters
mutation和getters接收的第一个参数是局部状态对象

const moduleA = {
    state: {
        count: 0
    },
    mutations: {
        addCount (state) {
            state.count++;
        }
    },
    getters: {
        doubleCount (state) {
            return state.count * 2;
        }
    }
}

const moduleB = {
}

const store = new Vuex.Store({
    state: {
        gCount: 111
    },
    modules: {
        a: moduleA,
        b: moduleB
    }
})
<script>
    export default {
        name: 'App',
        computed: {
            count () {
                return this.$store.getters.doubleCount;
            }
        },
        methods: {
            addCount () {
                this.$store.commit('addCount');
            }
        }
    }
</script>  

注意:
虽然, 我们的doubleCount和increment都是定义在对象内部的.
但是在调用的时候, 依然是通过this.$store来直接调用的.

Actions的写法

接收一个context参数对象
局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

const muduleA = {
    //...
    actions: {
        addCountSum ({state, commit, rootState}) {
            if ((state.count + rootState.count) / 2 === 1) {
                commit('addCount');
            }
        }
    }
}

getters的写法

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

项目结构

components文件夹  
store文件夹
  - index.js        -> 组装模块并导出 store的文件
  - actions.js      -> 根级别的 actions
  - mutations.js    -> 根级别的 mutations
  - modules            
      - aaaa.js     -> a模块  
      - bbbb.js     -> b模块