VueX

Vuex 状态管理器

一、Vuex 状态管理器

Vuex的原理

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

应用场景:Vue多个组件之间需要共享数据或状态。

Vuex有几个核心属性:State、Getter、Mutation、Action、Module。

Untitled

  • State:存储状态数据
  • Getter:从状态数据派生数据,相当于State的计算属性。
  • Mutation:存储用于同步更改状态数据的方法,默认传入的参数为state。
  • Action:存储用于异步更改状态数据,但不是直接更改,而是通过触发Mutation方法实现,默认参数为context。
  • Module:Vuex模块化。

全局配置Vuex

在 src 目录下创建 store 文件夹,并在里面创建一个index.js文件,然后index.js中配置如下:

// 第一步:引入Vue、和Vuex(固定写法)
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);

// 第二步:声明Vuex 的五个属性,其中state,mutations 是一定要定义的,其他的三个属性对象根据实际需要。
const state = {  // 初始化状态值--一定要有该属性对象
    ...
}
const mutations = {  // 自定义改变state初始值的方法--一定要有该属性对象
    ...
}
const getters = {  // 状态计算属性--该属性对象不是必须
    ...
}
const actions = { // 异步操作状态--该属性对象不是必须的
    ...
}
const modules = {  // 状态模块--该属性对象不是必须的
    ...
}

// 第三步:创建一个 store 实例,将声明的五个变量赋值赋值给 store 实例,如下:
const store = new Vuex.Store({
    state,
    mutations,
    //下面三个非必须
    getters,
    actions,
    modules
})

// 第四步:导出 store 实例,供外部访问
export default store

在项目的main.js中将Vuex注册到全局实例中

...
import store from './store'
...

new Vue({
    el: '#app',
    router,
    store,         //注入,组件中可以使用 this.$store 获取
    components: { App },
})

配置流程

梳理一下 Vuex 的配置流程:

  • 1.声明 state 对象,存放要初始状态的数据对象
  • 2.声明 getters 对象,存放 派生state中数据的方法(store计算属性)
  • 3.声明 mutations 对象,存放 设置 state中数据的方法(同步操作)
  • 4.声明 actions 对象,存放 异步操作 mutations中的方法的方法(异步操作)
  • 5.声明 modules 对象, 存放 外部模板 文件 (如果没有创建外部 js 文件,次步可去除)
  • 6.创建 Vuex 实例,注入上面声明的对象,并用export default导出,然后再在main.js中将 Vuex 挂在到Vue实例中

再简单看一下基本使用的语法:

  • 1.获取 某个状态的值: index.js 中的 obj 的值:this.$store.state.obj
    • index.js 中的 obj 的值:this.$store.getters.obj
    • page1.js 中的 arr 的值:this.$store.state[ "page1/arr" ]
    • page1.js 中的 arr 的值:this.$store.getters[ "page1/arr" ]
  • 2.设置 某个状态的值: index.js 中的 obj 的值:this.$store.commit( "obj" , val )
    • index.js 中的 obj 的值:this.$store.dispatch( "obj" , val ))
    • page1.js 中的 arr 的值:this.$store.commit( "page1/arr" , val ))
    • page1.js 中的 arr 的值:this.$store.dispatch( "page1/arr" , val )

各模块在流程中的功能

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块,由组件中的$store.dispatch('action 名称', data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法,由actions中的commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

Vue.sync的用法

日常开发时,我们总会遇到需要父子组件双向绑定的问题,但是考虑到组件的可维护性,vue中是不允许子组件改变父组件传的props值的。那么同时,vue中也提供了一种解决方案.sync修饰符。在此之前,希望你已经知道了vue中是如何通过事件的方式实现子组件修改父组件的data的。

.sync修饰符

首先我们知道,父组件通过绑定属性的方式向子组件传值,而在子组件中可以通过$emit向父组件通信,通过这种间接的方式改变父组件的data,从而实现子组件改变props的值。比如向下边这这样:

子组件使用$emit向父组件发送事件:

this.$emit('update:title', newTitle)

父组件监听这个事件并更新一个本地的数据title

<text-document
    :title="title"
    @update:title="val => title = val"
>

为了方便这种写法,vue提供了.sync修饰符,说白了就是一种简写的方式,我们可以将其当作是一种语法糖,比如v-on: click可以简写为@click。而上边父组件的这种写法,换成sync的方式就像下边这样:

<text-document
    :title.sync="title"
>

有没有发现很清晰,而子组件中我们的写法不变,其实这两种写法是等价的,只是一个语法糖而已,如果到这里你还不太明白。下边是个完整的demo,可以copy自己的项目中尝试一下。相信你会恍然大悟。

父组件

<template>
  <div>
    <child :name.sync="name"></child>
    <button @click="al">点击</button>
    <button @click="change">改变</button>
  </div>
</template>

<script>
import child from './child'
export default {
  name: 'list',
  components: {
    child
  },
  data () {
    return {
      listItems: ['buy food', 'play games', 'sleep'],
      name: 'xiaoming'
    }
  },
  methods: {
    al() {
      alert(this.name);
    },
    change() {
      this.name = '123';
    }
  }
}
</script>

子组件:

<template>
  <div>
    <input :value="name" @input="abc" type="text">
  </div>
</template>
<script>
export default {
  props: {
    name: {
      type: String,
      required: true
    }
  },
  methods: {
    abc(e) {
      console.log(e.target.value);
      this.$emit('update:name', e.target.value);
    }
  }
}
</script>

sync修饰符,与我们平常使用$emit实现父子组件通信没有区别,只不过是写法上方便一些。当然,利用引用类型的特性实现双向绑定我认为也并非完全不可取。

vuex的适用场景

在项目开发中,可能会有很多数据或者参数我们可能需要多次读取或者修改,像购物车等类似功能,这个时候我们的就可以用vuex来实现;vuex毕竟只是一个状态管理模式,状态管理模式是给我们提供方便的,但不是必需的,因为状态管理能做的事通过其他途径和办法也能实现。