简述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 相关文章推荐
使用Chrome浏览器调试AngularJS应用的方法
Jun 18 Javascript
Jquery实现顶部弹出框特效
Aug 08 Javascript
利用Angular.js限制textarea输入的字数
Oct 20 Javascript
Angular1.x自定义指令实例详解
Mar 01 Javascript
Vue响应式原理详解
Apr 18 Javascript
详解webpack 如何集成第三方js库
Jun 29 Javascript
自定义事件解决重复请求BUG的问题
Jul 11 Javascript
JavaScript实现body内任意节点的自定义属性功能示例
Sep 18 Javascript
使用Electron构建React+Webpack桌面应用的方法
Dec 15 Javascript
微信小程序下拉刷新PullDownRefresh的使用方法
Nov 29 Javascript
javascript局部自定义鼠标右键菜单
Dec 08 Javascript
Vue项目中如何封装axios(统一管理http请求)
May 02 Vue.js
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
解析link_mysql的php版
2013/06/30 PHP
php smarty truncate UTF8乱码问题解决办法
2014/06/13 PHP
php循环table实现一行两列显示的方法
2015/06/04 PHP
smarty模板判断数组为空的方法
2015/06/10 PHP
php 可变函数使用小结
2018/06/12 PHP
JAVASCRIPT keycode总结
2009/02/04 Javascript
JavaScript中exec函数用法实例分析
2015/06/08 Javascript
详解JavaScript中双等号引起的隐性类型转换
2016/05/30 Javascript
AngularJS入门教程之更多模板详解
2016/08/19 Javascript
js document.getElementsByClassName的使用介绍与自定义函数
2016/11/25 Javascript
Angular2 http jsonp的实例详解
2017/08/31 Javascript
Angular6笔记之封装http的示例代码
2018/07/27 Javascript
脚手架vue-cli工程webpack的作用和特点
2018/09/29 Javascript
jQuery实现input输入框获取焦点与失去焦点时提示的消失与显示功能示例
2019/05/27 jQuery
微信js-sdk 录音功能的示例代码
2019/11/01 Javascript
Python导入模块时遇到的错误分析
2017/08/30 Python
python按综合、销量排序抓取100页的淘宝商品列表信息
2018/02/24 Python
Python Web编程之WSGI协议简介
2018/07/18 Python
Python3基础教程之递归函数简单示例
2019/06/07 Python
python使用PIL剪切和拼接图片
2020/03/23 Python
将tf.batch_matmul替换成tf.matmul的实现
2020/06/18 Python
python 模拟登录B站的示例代码
2020/12/15 Python
python 录制系统声音的示例
2020/12/21 Python
canvas压缩图片以及卡片制作的方法示例
2018/12/04 HTML / CSS
标记环网Toke Ring IEEE802.5
2014/05/26 面试题
先进德育工作者事迹材料
2014/01/24 职场文书
小学毕业感言50字
2014/02/16 职场文书
班班通项目实施方案
2014/02/25 职场文书
责任担保书范文
2014/05/21 职场文书
优秀班集体事迹材料
2014/12/25 职场文书
外国人来华邀请函
2015/01/31 职场文书
博士生专家推荐信
2015/03/25 职场文书
安全生产会议制度
2015/08/06 职场文书
如何使用vue3打造一个物料库
2021/05/08 Vue.js
pytorch finetuning 自己的图片进行训练操作
2021/06/05 Python
讲解Python实例练习逆序输出字符串
2022/05/06 Python