1、为什么要用vuex
在vue组件通信的过程中,我们通信的目的往往就是在组件之间传递数据或组件的状态(这里将数据和状态统称为状态),进而更改状态。但可以看到如果我们通过最基本的方式来
进行通信,一旦需要管理的状态多了,代码就会变得十分混乱。对所有状态的管理便会显得力不从心,尤其是多人合作的时候。此时vuex出现了,他就是帮助我们把公用的状态全抽出来放
在vuex的容器中,然后根据一定的规则来进行管理。
2、概念
- 概念:vuex是一个状态管理工具,每一个Vuex应用的核心就是store(仓库);“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state),它的实例是唯一的-----单例模式; 一般把需要共享的数据放到store中;
- 功能:存数据、取数据、改数据;
- 出现场景:
- 涉及到非父子关系的组件,例如兄弟关系、祖孙关系,甚至更远的关系组件之间的联系;
- 在大型单页应用中,考虑如何更好地在组件外部管理状态;
- 注意:vuex的数据处理流程是一个"单向"的数据流 ;
- vuex的核心:
- state:存放数据(状态); 取数据 this.$store.state.变量名
- mutations:存放如何更改状态,同步方法放到mutations(同步)里面; 调用方法 this.$store.commit("方法名","参数")
- getters:相当于计算属性,从state中派生出状态,比如将state中的某个状态进行过滤然后获取新的状态; this.$store.getters.方法名(sum)
- actions:mutations的加强版,异步方法放在了actions(动作)里面; 调用方法this.$store.dispatch("方法名"),异步方法则会调用mutations里面的同步方法
- modules:就是当用这个容器来装这些状态还是显得混乱的时候,我们就可以把容器分成几块,把状态和管理规则分类来装。这和我们创建js模块是一个目的,让代码结构更清晰。
3、基本使用
- vuex的安装
cnpm i vuex --save
- 安装成功后,
- 新手入门 注意:vuex中是不允许直接修改store的状态值,我们必须通过mutations进行修改操作
- 新建store文件夹,内容如下
- 新手入门 注意:vuex中是不允许直接修改store的状态值,我们必须通过mutations进行修改操作
-
-
- 在app.js里面进行引入
import store from './store' new Vue({ router, i18n, store, el: '#app', render: h => h(App) })
- store文件夹中index.js的内容为
import Vue from 'vue' import Vuex from 'vuex' import state from './state' import actions from './actions' import mutations from './mutations' Vue.use(Vuex) export default new Vuex.Store({ state, actions, mutations })
- 在state.js中定义状态
export default { count: 1, }
- 在mutations.js里面定义同步方法
export default { // 改变状态的执行者 用于同步更改状态 stateMutationsAdd(state, payLoad) { // 第一个参数是state 第二个参数是调用mutations时传入的参数 state.count += payLoad; }, stateMutationsReduce(state, payLoad) { state.count -= payLoad; } }
- 在actions.js里面定义异步操作
export default { // actions并不直接更改状态 而是发起mutations来更改状态 stateAsyncReduce(context) { // context 是一个与 store 实例具有相同方法和属性的 context 对象 // 这个异步操作 我们可以发送http请求、定时器、 setTimeout(() => { context.commit("stateMutationsReduce", 5)//不能用this.$store,为undefined }, 1000) } }
- 在vue组件中使用(将state与getters结合进组件需要使用计算属性,将mutations与actions结合进组件需要在methods里面进行调用)
<template> <div class="home"> <div class="block"> <h1>vuex的基本使用</h1> <el-button size="mini" type="primary" @click="reduceNumber">-</el-button> {{count}} <el-button size="mini" type="primary" @click="addNumber">+</el-button> <h1>vuex练习结束</h1> </div> </div> </template> <script> export default { name: "home", data() { return {}; }, computed: { count() { return this.$store.state.count; } }, methods: { /** * [addNumber 对count数据进行增加操作 采用同步方式] * @return {[type]} [description] */ addNumber() { // 第一个参数是同步方法的名称 第二个参数是传递给方法的数据 this.$store.commit("stateMutationsAdd", 10); }, /** * [reduceNumber 对count数据进行减少操作 采用异步方式] * @return {[type]} [description] */ reduceNumber() { // dispatch返回的是actions执行的结果,是一个promise对象,如果异步操作之后还需要其他操作,可以使用.then/.catch等 this.$store.dispatch("stateAsyncReduce"); } }, mounted() {} }; </script>
- 效果图
- 在app.js里面进行引入
-
点击 + 进行同步增加
点击-进行异步减少,每隔1s减少5
-
- 新手入门之后,我们可以尝试将mutations里面的一些方法名称提取出来,从而提高代码维护性;
- state.js
export default { count: 1, }
- mutation-types.js
export const STATE_MUTATIONS_ADD = 'stateMutationsAdd' export const STATE_MUTATIONS_REDUCE = 'stateMutationsReduce'
- mutations.js
import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from './mutation-types' export default { // 改变状态的执行者 用于同步更改状态 [STATE_MUTATIONS_ADD](state, payLoad) { // 第一个参数是state 第二个参数是调用mutations时传入的参数 state.count += payLoad; }, [STATE_MUTATIONS_REDUCE](state, payLoad) { state.count -= payLoad; } }
- actions.js
import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from './mutation-types' export default { // actions并不直接更改状态 而是发起mutations来更改状态 stateAsyncReduce(context) { // context 是一个与 store 实例具有相同方法和属性的 context 对象 // 这个异步操作 我们可以发送http请求、定时器、 setTimeout(() => { context.commit(STATE_MUTATIONS_REDUCE, 5)//不能用this.$store,为undefined }, 1000) } }
- vue组件中的使用
<template> <div class="home"> <div class="block"> <h1>vuex的基本使用</h1> <el-button size="mini" type="primary" @click="reduceNumber">-</el-button> {{count}} <el-button size="mini" type="primary" @click="addNumber">+</el-button> <h1>vuex练习结束</h1> </div> </div> </template> <script> import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from "../store/mutation-types"; export default { name: "home", data() { return {}; }, computed: { count() { return this.$store.state.count; } }, methods: { /** * [addNumber 对count数据进行增加操作 采用同步方式] * @return {[type]} [description] */ addNumber() { // 第一个参数是同步方法的名称 第二个参数是传递给方法的数据 this.$store.commit(STATE_MUTATIONS_ADD, 10); }, /** * [reduceNumber 对count数据进行减少操作 采用异步方式] * @return {[type]} [description] */ reduceNumber() { // dispatch返回的是actions执行的结果,是一个promise对象,如果异步操作之后还需要其他操作,可以使用.then/.catch等 this.$store.dispatch("stateAsyncReduce"); } }, mounted() {} }; </script>
- state.js
- 为了方便起见,利用vuex提供的mapState、mapGetters、mapMutations以及mapActions四个方法将这些功能结合进组件
- 其他文件跟上述保持一致
- vue组件中的使用
<template> <div class="home"> <div class="block"> <h1>vuex的基本使用</h1> <el-button size="mini" type="primary" @click="stateAsyncReduce">-</el-button> {{count}} <p>使用getters{{name}}</p> <el-button size="mini" type="primary" @click="stateMutationsAdd(10)">+</el-button> <h1>vuex练习结束</h1> </div> </div> </template> <script> import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; export default { name: "home", data() { return {}; }, computed: { ...mapState(["count"]), }, methods: { ...mapActions(["stateAsyncReduce"]), ...mapMutations(["stateMutationsAdd"]) }, mounted() {} }; </script>
- 当你采用了上述方式进行整合之后,依旧存在过多的状态,导致代码混乱的现象,我们可以将store分割成模块(module),每个模块都拥有自己的state、getters、mutations以及actions,甚至是嵌套子模块,从上至下按照同样的方式进行分割。
- 首先,我们看一下目录结构
- 新手入门之后,我们可以尝试将mutations里面的一些方法名称提取出来,从而提高代码维护性;
-
-
- store/index.js
import Vue from 'vue' import Vuex from 'vuex' import * as actions from './actions' import modules from './modules' Vue.use(Vuex) export default new Vuex.Store ({ actions, modules, strict: false })
- store/mutation-types.js
export const STATE_MUTATIONS_ADD = 'stateMutationsAdd' export const STATE_MUTATIONS_REDUCE = 'stateMutationsReduce'
- store/actions.js(大型项目中我们可以将所有的异步操作提取出来)
import * as types from './mutation-types' const makeAction = (type) => { return ({ commit }, ...args) => commit(type, ...args) } export function stateAsyncReduce(context) { // context 是一个与 store 实例具有相同方法和属性的 context 对象 // 这个异步操作 我们可以发送http请求、定时器、 setTimeout(() => { context.commit(types.STATE_MUTATIONS_REDUCE, 5)//不能用this.$store,为undefined }, 1000) }
- module/index.js
const files = require.context('.', false, /.js$/) const modules = {} files.keys().forEach((key) => { if (key === './index.js') return modules[key.replace(/(./|.js)/g, '')] = files(key).default }) export default modules
- module/home.js(home可以自定义,以后项目中随机建立的模块名称)
import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from '../mutation-types' const state = { count: 1 } const mutations = { [STATE_MUTATIONS_ADD](state, payLoad) { // 第一个参数是state 第二个参数是调用mutations时传入的参数 state.count += payLoad; }, [STATE_MUTATIONS_REDUCE](state, payLoad) { state.count -= payLoad; } } export default { state, mutations }
- vue组件中的使用
<template> <div class="home"> <div class="block"> <h1>vuex的基本使用</h1> <el-button size="mini" type="primary" @click="stateAsyncReduce">-</el-button> <span>{{count}}</span> <el-button size="mini" type="primary" @click="stateMutationsAdd(10)">+</el-button> <h1>vuex练习结束</h1> </div> </div> </template> <script> import { mapState, mapGetters, mapMutations, mapActions } from "vuex"; export default { name: "home", data() { return {}; }, computed: { ...mapState({ count: state => state.home.count }) }, methods: { ...mapActions(["stateAsyncReduce"]), ...mapMutations(["stateMutationsAdd"]) }, mounted() {} }; </script>
- 特别强调的是:
...mapState({ count: state => state.home.count(home为模块的名称) })
- store/index.js
-
4、遇到的问题
暂无