简述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 相关文章推荐
js中传递特殊字符(+,&amp;)的方法
Jan 16 Javascript
jQuery 获取兄弟元素的几种不错方法
May 23 Javascript
jquery Validation表单验证使用详解
Sep 12 Javascript
javascript实现自动填写表单实例简析
Dec 02 Javascript
JavaScript电子时钟倒计时第二款
Jan 10 Javascript
JavaScript实现打开链接页面的方式汇总
Jun 02 Javascript
关于JavaScript中事件绑定的方法总结
Oct 26 Javascript
使用BootStrap实现表格隔行变色及hover变色并在需要时出现滚动条
Jan 04 Javascript
详解Vue中watch的高级用法
May 02 Javascript
微信小程序wepy框架学习和使用心得详解
May 24 Javascript
微信小程序 扭蛋抽奖机css3动画实现详解
Jul 19 Javascript
关于vue路由缓存清除在main.js中的设置
Nov 06 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利用COM对象访问SQLServer、Access
2006/10/09 PHP
SSI指令
2006/11/25 PHP
sql注入与转义的php函数代码
2013/06/17 PHP
php下Memcached入门实例解析
2015/01/05 PHP
Yii2创建表单(ActiveForm)方法详解
2016/07/23 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
PHP进阶学习之反射基本概念与用法分析
2019/06/18 PHP
在TP5数据库中四个字段实现无限分类的示例
2019/10/18 PHP
通过JAVASCRIPT读取ASP设定的COOKIE
2006/11/24 Javascript
JavaScript中Math对象使用说明
2008/01/16 Javascript
jQuery 获取URL参数的插件
2010/03/04 Javascript
jquery post方式传递多个参数值后台以数组的方式进行接收
2013/01/11 Javascript
多个表单中如何获得这个文件上传的网址实现js代码
2013/03/25 Javascript
javascript获取checkbox复选框获取选中的选项
2014/08/12 Javascript
在JS中操作时间之getUTCMilliseconds()方法的使用
2015/06/10 Javascript
JS实现兼容性较好的随屏滚动效果
2015/11/09 Javascript
每天一篇javascript学习小结(属性定义方法)
2015/11/19 Javascript
基于javascript实现页面加载loading效果
2020/09/15 Javascript
vue实现商品加减计算总价的实例代码
2018/08/12 Javascript
web页面和微信小程序页面实现瀑布流效果
2018/09/26 Javascript
JavaScript ES2019中的8个新特性详解
2019/02/20 Javascript
Python计算斗牛游戏概率算法实例分析
2017/09/26 Python
通过Python 获取Android设备信息的轻量级框架
2017/12/18 Python
python 判断参数为Nonetype类型或空的实例
2018/10/30 Python
详解基于python的多张不同宽高图片拼接成大图
2019/09/26 Python
在PyCharm中实现添加快捷模块
2020/02/12 Python
python语言的优势是什么
2020/06/17 Python
python3 re返回形式总结
2020/11/20 Python
python 将html转换为pdf的几种方法
2020/12/29 Python
YOOX美国官方网站:全球著名的多品牌时尚网络概念店
2016/09/11 全球购物
车间班组长岗位职责
2013/11/13 职场文书
中层竞聘演讲稿
2014/01/09 职场文书
公证委托书大全
2014/04/04 职场文书
英语教育专业毕业生求职信
2014/08/28 职场文书
我们的节日端午节活动总结
2015/02/11 职场文书
PyQt5 QThread倒计时功能的实现代码
2021/04/02 Python