简述vue状态管理模式之vuex


Posted in Javascript onAugust 29, 2018

了解vuex核心概念请移步 https://vuex.vuejs.org/zh/

一、初始vuex

1.1 vuex是什么

那么先来看看这两个问题:

什么是vuex?官网说:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。 按个人通俗理解来说就是:vuex就是用来管理各个组件之间的一些状态,可以理解为这些状态就是公共(共享)部分。此时任何组件都能从中获取状态或者触发一些行为事件。

什么情况下用到vuex?官网说:如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 global event bus 就足够您所需了。但是,如果您需要构建是一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

好,那么现在我就当你是开发一个比较大型的项目,在那些地方会用到vuex呢? 随着应用的复杂度增加,组件之间传递数据或组件的状态会越来越多,举个例子:当A组件进入B组件(A页面进入B页面)的时候,常常需要带一些参数过去,那么此时你可能会选择放在url后面当做参数传递过去,如果你不想轻易暴露参数,你有可能先存到session中或者localstorage中,然后进入到第二个页面的时候再取出来。不错,这确实是一种解决方法,而且用的不少。但这不是一个好的方法,这时候,你就需要vuex来帮助你了。另外,当你基本了解vuex的一些皮毛之后,你会发现vuex管理是基于模块化的思想,那么这就对项目后期管理维护很友好了。

so,现在你就得来深入认识一下vuex的核心概念了。下面是个人理解的概念,首先在此之前建议最好先把官方文档Vuex2.0概念过一遍。

vuex 就是把需要共享的变量全部存储在一个对象里面,然后将这个对象放在顶层组件中供其他组件使用

父子组件通信时,我们通常会采用 props + emit 这种方式。但当通信双方不是父子组件甚至压根不存在相关联系,或者一个状态需要共享给多个组件时,就会非常麻烦,数据也会相当难维护

1.2 vuex中有什么

const store = new Vuex.Store({
  state: {
    name: 'weish',
    age: 22
  },
  getters: {
    personInfo(state) {
      return `My name is ${state.name}, I am ${state.age}`;
    }
  }
  mutations: {
    SET_AGE(state, age) {
      commit(age, age);
    }
  },
  actions: {
    nameAsyn({commit}) {
      setTimeout(() => {
        commit('SET_AGE', 18);
      }, 1000);
    }
  },
  modules: {
    a: modulesA
  }
}

个就是最基本也是完整的 vuex 代码; vuex 包含有五个基本的对象

  1. state :存储状态。也就是变量;
  2. getters :派生状态。也就是 set 、 get 中的 get ,有两个可选参数: state 、 getters 分别可以获取 state 中的变量和其他的 getters 。外部调用方式: store.getters.personInfo() 。就和 vue 的 computed 差不多;
  3. mutations :提交状态修改。也就是 set 、 get 中的 set ,这是 vuex 中唯一修改 state 的方式,但不支持异步操作。第一个参数默认是 state 。外部调用方式: store.commit('SET_AGE', 18) 。和 vue 中的 methods 类似。
  4. actions :和 mutations 类似。不过 actions 支持异步操作。第一个参数默认是和 store 具有相同参数属性的对象。外部调用方式: store.dispatch('nameAsyn') 。
  5. modules : store 的子模块,内容就相当于是 store 的一个实例。调用方式和前面介绍的相似,只是要加上当前子模块名,如: store.a.getters.xxx()

1.3 vue-cli中使用vuex的方式

目录结构

├── index.html
├── main.js
├── components
└── store
  ├── index.js     # 我们组装模块并导出 store 的地方
  ├── state.js     # 跟级别的 state
  ├── getters.js    # 跟级别的 getter
  ├── mutation-types.js # 根级别的mutations名称(官方推荐mutions方法名使用大写)
  ├── mutations.js   # 根级别的 mutation
  ├── actions.js    # 根级别的 action
  └── modules
    ├── m1.js     # 模块1
    └── m2.js     # 模块2

state示例

const state = {
  name: 'weish',
  age: 22
};

export default state;

getter示例

getters.js 示例(我们一般使用 getters 来获取 state 的状态,而不是直接使用 state )

export const name = (state) => {
  return state.name;
}

export const age = (state) => {
  return state.age
}

export const other = (state) => {
  return `My name is ${state.name}, I am ${state.age}.`;
}

mutation-type示例

将所有 mutations 的函数名放在这个文件里

export const SET_NAME = 'SET_NAME';
export const SET_AGE = 'SET_AGE';

mutations示例

import * as types from './mutation-type.js';

export default {
  [types.SET_NAME](state, name) {
    state.name = name;
  },
  [types.SET_AGE](state, age) {
    state.age = age;
  }
};

actions示例

异步操作、多个 commit 时

import * as types from './mutation-type.js';

export default {
  nameAsyn({commit}, {age, name}) {
    commit(types.SET_NAME, name);
    commit(types.SET_AGE, age);
  }
}

modules?m1.js示例

如果不是很复杂的应用,一般来讲是不会分模块的

export default {
  state: {},
  getters: {},
  mutations: {},
  actions: {}
}

index.js示例(组装vuex)

import vue from 'vue';
import vuex from 'vuex';
import state from './state.js';
import * as getters from './getters.js';
import mutations from './mutations.js';
import actions from './actions.js';
import m1 from './modules/m1.js';
import m2 from './modules/m2.js';
import createLogger from 'vuex/dist/logger'; // 修改日志

vue.use(vuex);

const debug = process.env.NODE_ENV !== 'production'; // 开发环境中为true,否则为false

export default new vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: {
    m1,
    m2
  },
  plugins: debug ? [createLogger()] : [] // 开发环境下显示vuex的状态修改
});

最后将 store 实例挂载到 main.js 里面的 vue 上去就行了

import store from './store/index.js';

new Vue({
 el: '#app',
 store,
 render: h => h(App)
});

在 vue 组件中使用时,我们通常会使用 mapGetters 、 mapActions 、 mapMutations ,然后就可以按照 vue 调用 methods 和 computed 的方式去调用这些变量或函数,示例如

import {mapGetters, mapMutations, mapActions} from 'vuex';

/* 只写组件中的script部分 */
export default {
  computed: {
    ...mapGetters([
      name,
      age
    ])
  },
  methods: {
    ...mapMutations({
      setName: 'SET_NAME',
      setAge: 'SET_AGE'
    }),
    ...mapActions([
      nameAsyn
    ])
  }
};

二、modules

在 src 目录下 , 新建一个 store 文件夹 , 然后在里面新建一个 index.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);

export default new vuex.Store({
  state:{
    show:false
  }
})

在 main.js 里的代码应该改成,在实例化 Vue 对象时加入 store 对象

//vuex
import store from './store'

new Vue({
 el: '#app',
 router,
 store,//使用store
 template: '<App/>',
 components: { App }
})

这样就把 store 分离出去了 , 那么还有一个问题是 : 这里 $store.state.show 无论哪个组件都可以使用 , 那组件多了之后 , 状态也多了 , 这么多状态都堆在 store 文件夹下的 index.js 不好维护怎么办 ?

我们可以使用 vuex 的 modules , 把 store 文件夹下的 index.js 改成

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);

import dialog_store from '../components/dialog_store.js';//引入某个store对象

export default new vuex.Store({
  modules: {
    dialog: dialog_store
  }
})

这里我们引用了一个 dialog_store.js , 在这个 js 文件里我们就可以单独写 dialog 组件的状态了

export default {
  state:{
    show:false
  }
}

做出这样的修改之后 , 我们将之前我们使用的 $store.state.show 统统改为 $store.state.dialog.show 即可

如果还有其他的组件需要使用 vuex , 就新建一个对应的状态文件 , 然后将他们加入 store 文件夹下的 index.js 文件中的 modules 中

modules: {
  dialog: dialog_store,
  other: other,//其他组件
}

三、mutations

对 vuex 的依赖仅仅只有一个 $store.state.dialog.show 一个状态 , 但是如果我们要进行一个操作 , 需要依赖很多很多个状态 , 那管理起来又麻烦了

mutations 里的操作必须是同步的

export default {
  state:{//state
    show:false
  },
  mutations:{
    switch_dialog(state){//这里的state对应着上面这个state
      state.show = state.show?false:true;
      //你还可以在这里执行其他的操作改变state
    }
  }
}

使用 mutations 后 , 原先我们的父组件可以改为

<template>
 <div id="app">
  <a href="javascript:;" rel="external nofollow" rel="external nofollow" @click="$store.commit('switch_dialog')">点击</a>
  <t-dialog></t-dialog>
 </div>
</template>

<script>
import dialog from './components/dialog.vue'
export default {
 components:{
  "t-dialog":dialog
 }
}
</script>

使用 $store.commit('switch_dialog') 来触发 mutations 中的 switch_dialog 方法

四、actions

多个 state 的操作 , 使用 mutations 会来触发会比较好维护 , 那么需要执行多个 mutations 就需要用 action 了

export default {
  state:{//state
    show:false
  },
  mutations:{
    switch_dialog(state){//这里的state对应着上面这个state
      state.show = state.show?false:true;
      //你还可以在这里执行其他的操作改变state
    }
  },
  actions:{
    switch_dialog(context){//这里的context和我们使用的$store拥有相同的对象和方法
      context.commit('switch_dialog');
      //你还可以在这里触发其他的mutations方法
    },
  }
}

那么 , 在之前的父组件中 , 我们需要做修改 , 来触发 action 里的 switch_dialog 方法

<template>
 <div id="app">
  <a href="javascript:;" rel="external nofollow" rel="external nofollow" @click="$store.dispatch('switch_dialog')">点击</a>
  <t-dialog></t-dialog>
 </div>
</template>

<script>
import dialog from './components/dialog.vue'
export default {
 components:{
  "t-dialog":dialog
 }
}
</script>

使用 $store.dispatch('switch_dialog') 来触发 action 中的 switch_dialog 方法。

官方推荐 , 将异步操作放在 action 中

五、getters

getters 和 vue 中的 computed 类似 , 都是用来计算 state 然后生成新的数据 ( 状态 ) 的

假如我们需要一个与状态 show 刚好相反的状态 , 使用 vue 中的 computed 可以这样算出来

computed(){
  not_show(){
    return !this.$store.state.dialog.show;
  }
}

那么 , 如果很多很多个组件中都需要用到这个与 show 刚好相反的状态 , 那么我们需要写很多很多个 not_show , 使用 getters 就可以解决这种问题

export default {
  state:{//state
    show:false
  },
  getters:{
    not_show(state){//这里的state对应着上面这个state
      return !state.show;
    }
  },
  mutations:{
    switch_dialog(state){//这里的state对应着上面这个state
      state.show = state.show?false:true;
      //你还可以在这里执行其他的操作改变state
    }
  },
  actions:{
    switch_dialog(context){//这里的context和我们使用的$store拥有相同的对象和方法
      context.commit('switch_dialog');
      //你还可以在这里触发其他的mutations方法
    },
  }
}

我们在组件中使用 $store.state.dialog.show 来获得状态 show , 类似的 , 我们可以使用 $store.getters.not_show 来获得状态 not_show

注意 : $store.getters.not_show 的值是不能直接修改的 , 需要对应的 state 发生变化才能修改

六、mapState、mapGetters、mapActions

很多时候 , $store.state.dialog.show 、 $store.dispatch('switch_dialog') 这种写法很不方便

使用 mapState 、 mapGetters 、 mapActions 就不会这么复杂了

<template>
 <el-dialog :visible.sync="show"></el-dialog>
</template>

<script>
import {mapState} from 'vuex';
export default {
 computed:{

  //这里的三点叫做 : 扩展运算符
  ...mapState({
   show:state=>state.dialog.show
  }),
 }
}
</script>

相当于

<template>
 <el-dialog :visible.sync="show"></el-dialog>
</template>

<script>
import {mapState} from 'vuex';
export default {
 computed:{
  show(){
    return this.$store.state.dialog.show;
  }
 }
}
</script>

mapGetters 、 mapActions 和 mapState 类似 , mapGetters 一般也写在 computed 中 , mapActions 一般写在 methods 中

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript - HTML的request类
Jul 15 Javascript
php上传图片并给图片打上透明水印的代码
Jun 07 Javascript
Jquery上传插件 uploadify v3.1使用说明
Jun 18 Javascript
Jquery多选框互相内容交换的实例代码
Jul 04 Javascript
jQuery实现滚动鼠标放大缩小图片的方法(附demo源码下载)
Mar 05 Javascript
JavaScript程序中实现继承特性的方式总结
Jun 24 Javascript
Bootstrap基本样式学习笔记之表格(2)
Dec 07 Javascript
微信小程序中this.data与this.setData的区别详解
Sep 17 Javascript
详解关于html,css,js三者的加载顺序问题
Apr 10 Javascript
React.js组件实现拖拽排序组件功能过程解析
Apr 27 Javascript
vue全局使用axios的操作
Sep 08 Javascript
js实现限定区域范围拖拉拽效果
Nov 20 Javascript
jQuery实现的简单手风琴效果示例
Aug 29 #jQuery
angularjs实现对表单输入改变的监控(ng-change和watch两种方式)
Aug 29 #Javascript
jQuery实现的淡入淡出图片轮播效果示例
Aug 29 #jQuery
JS实现数组的增删改查操作示例
Aug 29 #Javascript
vue-video-player 通过自定义按钮组件实现全屏切换效果【推荐】
Aug 29 #Javascript
vue-cli3.0使用及部分配置详解
Aug 29 #Javascript
vue2实现搜索结果中的搜索关键字高亮的代码
Aug 29 #Javascript
You might like
控制PHP的输出:缓存并压缩动态页面
2013/06/11 PHP
php数组保存文本与文本反编成数组实例
2014/11/13 PHP
PHP获取HTTP body内容的方法
2018/12/31 PHP
Javascript数组的排序 sort()方法和reverse()方法
2012/06/04 Javascript
jquery $(&quot;#variable&quot;) 循环改变variable的值示例
2014/02/23 Javascript
JS限制文本框只能输入数字和字母方法
2015/02/28 Javascript
AngularJS 自定义过滤器详解及实例代码
2016/09/14 Javascript
实例解析Array和String方法
2016/12/14 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
jQuery选择器之表单元素选择器详解
2017/09/19 jQuery
JavaScript实现写入文件到本地的方法【基于FileSaver.js插件】
2018/03/15 Javascript
react配合antd组件实现的管理系统示例代码
2018/04/24 Javascript
layer.confirm取消按钮绑定事件的方法
2018/08/17 Javascript
vue.js实现三级菜单效果
2019/10/19 Javascript
layui写后台表格思路和赋值用法详解
2019/11/14 Javascript
[02:35]DOTA2超级联赛专访XB 难忘一年九冠称王
2013/06/20 DOTA
[01:02:17]2014 DOTA2华西杯精英邀请赛 5 24 DK VS VG
2014/05/26 DOTA
12步入门Python中的decorator装饰器使用方法
2016/06/20 Python
python使用原始套接字发送二层包(链路层帧)的方法
2019/07/22 Python
Python实现银行账户资金交易管理系统
2020/01/03 Python
Python unittest单元测试框架及断言方法
2020/04/15 Python
如何在python中处理配置文件代码实例
2020/09/27 Python
使用CSS3设计地图上的雷达定位提示效果
2016/04/05 HTML / CSS
前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)
2018/07/12 HTML / CSS
详解html5 postMessage解决跨域通信的问题
2018/08/17 HTML / CSS
英国复古服装购物网站:Collectif
2019/10/30 全球购物
英国珠宝和手表专家:Pleasance & Harper
2020/10/21 全球购物
资产经营总监岗位职责
2013/12/04 职场文书
公司授权委托书范本
2014/04/03 职场文书
协议书的格式
2014/04/23 职场文书
2014年教师节国旗下讲话稿
2014/09/10 职场文书
2015年双拥工作总结
2015/04/08 职场文书
2015年林业工作总结
2015/05/14 职场文书
2015年国庆节标语大全
2015/07/30 职场文书
2019职场实习报告该怎么写?
2019/07/01 职场文书
农村房屋租赁合同(范本)
2019/07/23 职场文书