[TOC]

干嘛的?

用于解决组件之间数据共享,数据的集中处理,是一种状态管理的设计模式。

什么是vuex ?单项数据流

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理器,采用 集中式存储 管理所有组件的状态。

vuex的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?

  • 因为vuex生成了一个store实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。
  • store实例上有数据,有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据,导致另一个组件上的数据也会改变,就像是一个对象的引用。

四、Vuex - 图1

基本使用

#安装
yarn add vuex
#引入 一般我们会在src根目录下创建一个store的文件夹,
#下面写index.js来实现相关的存储方法(组件共用数据)
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

vuex的组成部分(应用结构)

四、Vuex - 图2

vuex的工作流程

1.通过this.$store.dispatch()向vuex中的actions派发一个事件同时传递一个参数
methods:{
        handleClick(city){
            /* this.$store.dispatch 向vuex派发一个事件,同时传递一个参数 */
            this.$store.dispatch("changeCity",city)
        }
}
2.vuex-actions中接收dispatch派发过来的事件和参数
export default new Vuex.Store({
  ...
  actions: {
    changeCity(ctx,city){
      /* ctx表示上下文  this.$store
      city是自定义事件传递过来的参数 */
     3.在actions中使用commit方法向mutations提交一个事件,同时传递一个参数
      ctx.commit("toggleCity",city)
    }
  }
})
4.在mutations中接收commit方法提交过来的事件,同时改变state中的状态
export default new Vuex.Store({
  state: {
    city:"武汉"
  },
  mutations: {
    toggleCity(state,city){
      state.city = city;
    }
  }
})

数据持久化 =>设置缓存机制

function getCity(){
   let defaultCity = "天门";
   if(localStorage.getItem("city")){
     defaultCity = localStorage.getItem("city")
   }
   return defaultCity;
}
export default new Vuex.Store({
  state: {
    city:getCity()
  },
  mutations: {
    changeCity(state,city){
      state.city = city;
    }
  },
  actions: {
    changeCity(ctx,city){
      ctx.commit("changeCity",city)
        //1.设置缓存
      localStorage.setItem("city",city)
    }
  }
})

State

State负责存储整个应用的状态数据,一般需要在使用的时候在跟节点注入store对象,后期就可以使用this.$store.state直接获取状态。一般是在main.js文件中引入store的文件,从而使用。(vue spa应用中)

//store为实例化生成的
import store from './store'
new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

这个store可以理解为一个容器,包含着应用中的state等。实例化生成store的过程是:

const mutations = {...};
const actions = {...};
const state = {...};
Vuex.Store({
  state,
  actions,
  mutation
});

后续在组件中使用的过程中,如果想要获取对应的状态你就可以直接使用this.$store.state获取,当然,也可以利用vuex提供的mapState辅助函数将state映射到计算属性中去,如

mapState

state: {
    count:0,
    city:"武汉",
    name:"港啊",
    age:20,
    sex:"男"
  },

<template>
  <div class="home">
    <h2 >{{city}}</h2>
    <h2 >{{age}}</h2>
  </div>
</template>

<script>
import {mapState} from 'vuex';
export default {
  name: 'home',
  computed:{
    ...mapState(['city','name','age','sex'])
  }
}
</script>

image.png

  • 映射

image.png

Mutations

Mutations的中文意思是“变化”,利用它可以更改状态,本质就是用来处理数据的函数,其接收唯一参数值state。store.commit(mutationName)是用来触发一个mutation的方法。需要记住的是,定义的mutation必须是同步函数,否则devtool中的数据将可能出现问题,使状态改变变得难以跟踪。
| 直接派发一个事件给mutations

export default {
  methods: {
    handleClick() {
      this.$store.commit('changeCity')
    }
  }
}
mutations:{
    changeCity(state){
        state.city="成都"
    }
}

或者使用辅助函数mapMutations直接将触发函数映射到methods上,这样就能在元素事件绑定上直接使用了。如:

mapMutations

<template>
  <div class="home">
    <h2>{{this.$store.state.count}}</h2>
    <button @click="myIncrease">增加</button>
    <button @click="myDecrease">减少</button>
  </div>
</template>

<script>
import {mapMutations} from 'vuex';
export default {
  name: 'home',
  methods:{
     ...mapMutations(['myIncrease','myDecrease']) 
  }
}
</script>

 mutations: {
    myIncrease(state){
      state.count+=1
    },
    myDecrease(state){
      state.count-=1
    }
  },

image.png

  • 映射

image.png

Actions

Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。其辅助函数是mapActions与mapMutations类似,也是绑定在组件的methods上的。如果选择直接触发的话,使用this.$store.dispatch(actionName)方法。action内部支持异步方法,这是很重要的一点区别。

mapActions

export default new Vuex.Store({
  state: {
    count:0
  },
  /* 异步操作,处理一些复杂的业务逻辑 */
  actions: {
    increase(ctx){
      ctx.commit("myIncrease")
    },
    decrease(ctx){
      ctx.commit("myDecrease")
    }
  },
  /* 简单的更改状态(state)的逻辑*/
  mutations: {
     myIncrease(state){
       state.count+=1;
     },
     myDecrease(state){
       state.count-=1;
     }
  },
})
  • 映射 ```css
``` ## Getters 有些状态需要做二次处理,就可以使用getters。通过this.$store.getters.valueName对派生出来的状态进行访问。或者直接使用辅助函数mapGetters将其映射到本地计算属性中去。这个属性相当于vuex的计算属性。 ```css /* 对state中的数据进行再次处理 */ getters:{ myCount(state){ return "当前的数量:"+state.count; } } ``` ### mapGetters ```css //mapGetters 将 store 中的 getter 映射到局部计算属性 import {mapGetters} from 'vuex'; export default { name: 'home', computed:{ ...mapGetters(['myCount']) } } ``` ## Plugins 插件就是一个钩子函数,在初始化store的时候引入即可。比较常用的是内置的logger插件,用于作为调试使用。 ``` import createLogger from 'vuex/dist/logger' const store = Vuex.Store({ ... plugins: [createLogger()] }) ``` ## modules 随着项目的复杂性增加,我们共享的状态越来越多,这时候我们就需要把我们状态的各种操作进行一个分组,分组后再进行按组编写。
作用:当store对象非常复杂的时候,我们可以根据将其拆分成不同的模块 ### 1. 新建modules文件夹,拆分module ![module.png](https://cdn.nlark.com/yuque/0/2019/png/402644/1574323726476-dd6e883b-6aff-479c-93cc-452955ffab4d.png#align=left&display=inline&height=380&name=module.png&originHeight=380&originWidth=563&size=22125&status=done&style=none&width=563) ```javascript //user.js const user = { state: { name:"程超" }, mutations:{}, actions: { }, getters: { } } export default user; ``` ```javascript //info.js const info = { state: { tips:12, news:15 }, mutations: { add(state){ state.tips++; }, reduce(state){ state.tips--; } }, actions: { }, getters: { } } export default info; ``` ```javascript //index.js export default new Vuex.Store({ modules:{ user, info } }) ``` ### 2. 使用数据 ```javascript ``` ## 注意事项 ### 更新数据 正如文档中所说,我们应该将所有的数据修改都定义在mutations中,这才是科学正确的做法。 ### state更改报错 Error in v-on handler: "Error: [vuex] use store.replaceState()
to explicit replace store state."
Error: [vuex] use store.replaceState() to
explicit replace store state. 报错如上,是因为修改state的时候,写成了this.state = data ,正确的写法应该是 this.state.xxx =data ; ### vuex与持久存储localStorage的关系 **场景** :发现部分开发在引入持久存储之后,mutation的时候没有改变state,而是直接修改的localStorage,这样会导致没有页面刷新时,拿到的state都是不对的。因为state不会实时监听localStorage的更改。
**解决方案** :
1 更改state的时候,除了更改localStorage,也要更改state,而且更改state才是必须要做的,而更改localStorage只是为了解决刷新页面后数据丢失的问题。(推荐方式,毕竟使用vuex的全套才是正宗)
2 如果你觉得上面的方案每次都写很麻烦,也可以尝试增加locastorage的监听机制,当发生变化的时候,寻找更改对应的state. ### 拓展符写法 在mapState,mapActions,mapMutations使用的时候,为了不影响正常方法的使用,我们可以使用对象以及数组的拓展方法进行拓展。 ``` methods:{ ...mapMutations([ 'add','reduce' ]), ...mapActions(['addAction','reduceAction']) }, // 获取state属性,你也可以采用这种写法 import {mapState} from 'vuex'; computed:mapState({ count:state=>state.count }) ``` ### 单页中直接定义使用(特别说明) ``` // 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) //使用的时候 如果想改变数值,必须用事件触发,不是直接改变。 store.commit('increment') console.log(store.state.count) // -> 1 ``` ## 个人理解 自我实践下来,如果要用好vuex,需要掌握以下的几点。
1 组件数据共享,跨页面数据共享,可以统一管理数据的存储,操作,分发。比如用户数据,比如固定的某些数据是固定某些api获取的且不止用于一个组件或者一个场景。
2 不要形式主义,每个页面都用vuex;每个模块都写getters,actions,常量方法,当你的工程量、数据量达到使用某技术场景的时候,采用某方案会觉得恰如其分
3 处理基于数据的业务逻辑,一般是跨页面跨组件的,比如购买流程对用户余额,购物车,订单的联动影响
4 vuex核心的index做一些模块公用的存储工具,可以配置一些需要的插件或者工具类
5 拓展:数据通讯不止vuex,简单的也可以用event bus,甚至页面内的已经能符合你的需求了
6 actions,支持异步是指更加建议在action中写异步,写复杂的逻辑,而所有的更改state建议写在mutations ## 使用vuex的核心概念 每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。 ### vuex的核心概念和核心概念图: 1、state - Vuex store实例的根状态对象,用于定义共享的状态变量。
2、Action -动作,向store发出调用通知,执行本地或者远端的某一个操作(可以理解为store的methods)
3、Mutations -修改器,它只用于修改state中定义的状态变量。
4、getter -读取器,外部程序通过它获取变量的具体值,或者在取值前做一些计算(可以认为是store的计算属性) ### Vuex的应用场景: Vuex主要用于:
1、多层嵌套的组件之间进行状态传递
2、兄弟组件间进行状态传递时(当然也可以使用中央事件总线BUS)
3、多组件共享状态时

更为具体的场景:组件之间的状态、音乐播放、登录状态、加入购物车...![288a0dc913bab3fe765baf18fb4bac27_701x551.png](https://cdn.nlark.com/yuque/0/2020/png/402644/1583220449909-28a05844-6698-470a-96fa-ac240fe36d0f.png#align=left&display=inline&height=551&name=288a0dc913bab3fe765baf18fb4bac27_701x551.png&originHeight=551&originWidth=701&size=8091&status=done&style=none&width=701) ### 代码: ```javascript //vuex的使用 #1.安装vuex npm i vuex -S #2.main.js import Vue from 'vue' import Vuex from 'vuex' // 注册vuex到vue中 Vue.use(Vuex) // new Vuex.Store()实例,得到一个数据仓储对象 // 可以在组件中通过this.$store.state.xx 来访问store中的数据 var store = new Vuex.Store({ //state相当于组件中的data state: { count: 0 }, //如果要修改store中state的值,需要调用 mutations提供的方法,可以通过this.$store.commit('方法名')来调用 mutations: { increment(state) { state.count++ }, //mutations函数参数列表中最多支持两个参数,其中参数1是state; 参数2是通过commit提交过来的参数; subtract(state, obj) { console.log(obj) state.count -= obj.step; } }, getters: { //这里的getters只负责对外提供数据,不负责修改数据,如果想要修改 state 中的数据需要在mutations中修改 optCount: function (state) { return '当前最新的count值是:' + state.count } } }) // 总结: // 1. state中的数据,不能直接修改,如果想要修改,必须通过 mutations // 2. 如果组件想要直接 从 state 上获取数据: 需要 this.$store.state.*** // 3. 如果组件想要修改数据,必须使用 mutations 提供的方法,需要通过 this.$store.commit('方法的名称', 唯一的一个参数) // 4. store中state上的数据在对外提供的时候建议做一层包装,推荐使用 getters。调用的时候则用this.$store.getters.*** import App from './App.vue' const vm = new Vue({ el: '#app', render: c => c(App), //将vuex创建的store挂载到VM实例上,只要挂载到了 vm 上,任何组件都能使用store来存取数据 store }) #3.index.html
#4.App.vue

5.components/amount.vue

6.components/counter.vue

```