VueX 笔记

Vuex 介绍

前置知识:

简介

什么是Vuex

官网文档

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

%title插图%num

Vue 它实际是解决非关系数据之间传输或者不同组件数据共享的问题,通过几个部分进行数据之间的交互:

  • 状态,驱动应用的数据源;
  • 视图,以声明方式将状态映射到视图;
  • 操作,响应在视图上的用户输入导致的状态变化。

适用范围

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

如果您打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。如果应用够简单,最好不要使用 Vuex。
一个简单的 store 模式就足够所需了。但是,如果需要构建一个中大型单页应用,可能会考虑如何更好地在组件外部管理状态,
Vuex 将会成为自然而然的选择。

安装

前期准备:需要提前安装 vue/cil 脚手架

1. 使用包管理工具 下载

PowerShell
vue create demo
cd demo

# yarn 下载
yarn add vuex@3.5.1 --save
# npm 下载
npm install vuex@3.5.1 --save

2. 在main.js 中配置 Vuex

JavaScript
import Vue from 'vue'
import App from './App.vue'
// 导入 Vuex
import Vuex from 'vuex';

Vue.config.productionTip = false
// 在Vue中使用 Vuex (相当于调用 vuex 的 install 函数 )
Vue.use(Vuex);
// 实例化 vex 并进行配置
const store = new Vuex.Store({});

new Vue({
  render: h => h(App),
  // Vuex 注入到 vue实例
  store
}).$mount('#app')
Vuex 基础

通过掌握基础使用来逐渐了解 Vuex 使用原理

state

简介

state是放置所有公共状态的属性,如果有一个公共状态数据,只需要定义在 state 对象中

定义

通过 main.js 的 Vuex.Store({}) 内定义

main.js
const store = new Vuex.Store({
  state:{
    // 管理数据
    count:10,
  },
});

使用

state的使用分为 原始形式 和 计算属性 还有 辅助函数

原始使用 – 插值表达式

组件可以使用 this.$store 获取到 vuex 中的 store 对象实例,可通过 state 属性获取 count

App.vue
<template>
    <div>store的数据:{{ $store.state.count }}</div>
</template>
mian.js
...
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state:{
    count:10,
  },
});
...

效果展示

%title插图%num

计算属性

将 state 属性定义在计算属性中

App.vue
<template>
    <div>store的数据:{{ count }}</div>
</template>
export default {
  computed: {
    count() {
      return this.$store.state.count;
    }
  }
}
main.js
...
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state:{
    count:10,
  },
});
...

效果展示

%title插图%num

辅助函数 – mapState

mapState 是辅助函数,通过该函数可帮助我们把 store 中的数据映射到组件的 计算属性 中

App.vue
<template>
  <div>store的数据:{{ count }}</div>
</template>

<script>
import { mapState } from 'vuex';
export default {
  computed: {
    // 采用数组形式引入 state 属性
    ...mapState(['count'])
  }
}
</script>
main.js
...
import Vuex from 'vuex';

Vue.use(Vuex);

const store = new Vuex.Store({
  state:{
    count:10,
  },
});
...

效果展示

%title插图%num
mutations

简介

state 数据修改只能通过 mutations,并且 mutation 必须是同步更新,目的是形成快照

数据快照:一次 mutation 的执行,立刻得到一种视图状态,因为是立刻,所以必须是同步
注意: Vuex中 mutaions 中要求不能写异步代码,如果有异步的 ajax请求,应该放在 actions 中

定义

通过 main.js 的 Vuex.Store({}) 内定义

main.js
const store = new Vuex.Store({
  state: {
    count: 10,
  },
  // 修改 state 必须通过 mutations
  mutations: {
    
    }
  }
});

格式说明

mutations 是一个对象,对象中存放着修改 state 的方法。它接收两个参数:

  • 参数1:store 对象,通过该参数可以直接获取 store 中的 state 存储的值
  • 参数2:payload载荷,允许在使用 mutations 的方法中向函数中传递参数
main.js
const store = new Vuex.Store({
  state: {
    count: 10,
  },
  mutations: {
    // 参数一 存放当前 store 的 state 属性
    // 参数二 是 payload载荷,调用 mutaiions 用于传递参数
    addCount(state, num) {
      state.count += num;
    }
  }
});

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

使用

mutations 的使用分为 原始形式 和 辅助函数,并可以对 mutations 中的方法进行自定义传参

原始形式 – this.$store.commit(方法名)

通过 commit 写入对应的字符串函数名调用对应 mutations 方法

App.vue
<template>
  <div id="app">
    <div>store的数据:{{ count }}</div>
    <button @click="addCount">+1</button>
  </div>
</template>
<script>
import { mapState } from 'vuex';
export default {
  computed: {
    ...mapState(['count'])
  },
  methods:{
    addCount(){
      this.$store.commit('addCount');
    },
    
  }
  
}
</script>

效果展示

%title插图%num

辅助函数 – mapMutations()

mapMutations 是辅助函数,通过该函数可帮助我们把 mutations 中的方法映射到组件的 methods 中

App.vue
<template>
  <div id="app">
    <div>store的数据:{{ count }}</div>
    <button @click="addCount">+1</button>
  </div>
</template>

<script>
import { mapState } from 'vuex';
import { mapMutations } from 'vuex';
export default {
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapMutations(['addCount'])
  }
}
</script>

效果展示

%title插图%num

函数传参

通过 mutations 中的方法的参数2定义和接收,对函数进行自由传参;
注意:如若 mutations 方法定义了形参,但是调用方不对该函数传参,vue会默认在形参中传入事件对象

main.js
const store = new Vuex.Store({
  state: {
    count: 10,
  },
  mutations: {
    addCount(state, num = 1) {
      state.count += num;
    }
  }
});
App.vue
<template>
  <div id="app">
    <div>store的数据:{{ count }}</div>
    <button @click="addNum">原始形式方式 +5</button>
    <button @click="addCount(3)">辅助函数方式 +3</button>
  </div>
</template>

<script>
import { mapState } from 'vuex';
import { mapMutations } from 'vuex';
export default {
  computed: {
    ...mapState(['count'])
  },
  methods: {
    addNum() {
      this.$store.commit('addCount', 5);
    },
    ...mapMutations(['addCount']),
  }
}
</script>

效果展示

%title插图%num
actions

简介

actions 是负责 vuex 中主要负责进行异步操作

state 是存放数据的,mutations 是同步更新数据,actions 则负责进行异步操作

定义

通过 main.js 的 Vuex.Store({}) 内定义

main.js
const store = new Vuex.Store({
  state: {
    ...
  },
  mutations: {
    ...
  },
  actions: {
    getAsyncCount() {
     ...
    }
  }
});

格式说明

在 actions 中定义的异步数据,取得的数据一般为 ajax请求得到的结果,不会立即返回,
当返回数据后,actions 中的方法通过调用 mutations 方法即可实现修改 state 中的数据。

actions 中定义的方法接收两个参数:

  • 参数1:context 表示当前 store 的实例,可以通过 context.state 获取状态,
    也可以通过 context.commit 来提交 mutations。也可以通过 context.diapatch 调用其他的 action
  • 参数2:payload载荷,允许在使用 actions 的方法中向函数中传递参数
main.js
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    addCount(state) {
      state.count++;
    }
  },
  actions: {
    getAsyncCount(context) {
      setTimeout(() => {
        // 3秒后 执行 addCount 方法
        context.commit('addCount');
      }, 3000);
    }
  }
});

使用

actions 使用方式分为 原始调用 和 辅助函数,并可以对 actions 中的方法进行自定义传参

原始调用 – this.$store.dispatch(方法名);

actions 会等待异步的结果返回,得到结果后可以调用 mutations 中的方法立即执行

App.vue
<template>
  <div>
    <div>store的数据:{{ $store.state.count }}</div>
    <button @click="asyncAddNum">等待3秒后 +1</button>
  </div>
</template>

<script>
export default {
  methods: {
    asyncAddNum() {
      this.$store.dispatch('asyncAddCount');
    },
  },
}
</script>
main.js
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    addCount(state) {
      state.count++;
    },
  },
  actions: {
    asyncAddCount(context) {
      // 模拟异步执行
      setTimeout(() => {
        // 调用 mutations 中的方法
        context.commit('addCount');
      }, 3000);
    },
  },
});

效果展示

%title插图%num

辅助函数 – mapActions()

mapActions 是辅助函数,通过该函数可帮助我们把 actions 中的方法映射到组件的 methods 中

App.vue
<template>
  <div id="app">
    <div>store的数据:{{ $store.state.count }}</div>
    <button @click="asyncAddCount">等待3秒后 +1</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex';
export default {
  methods: {
    ...mapActions(['asyncAddCount']),
  },
}
</script>
main.js
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    addCount(state) {
      state.count++;
    },
  },
  actions: {
    asyncAddCount(context) {
      // 模拟异步执行
      setTimeout(() => {
        // 调用 mutations 中的方法
        context.commit('addCount');
      }, 3000);
    },
  },
});

效果展示

%title插图%num

函数传参

通过 actions 中的方法的参数2定义和接收,对函数进行自由传参;
注意:如若 actions 中的方法定义了形参,但是调用方不对该函数传参,vue会默认在形参中传入事件对象

App.vue
<template>
  <div id="app">
    <div>store的数据:{{ $store.state.count }}</div>
    <button @click="asyncAdd">原始调用 等待3秒后 +233</button>
    <button @click="asyncAddCount(233)">辅助函数 等待3秒后 +233</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex';
export default {
  methods: {
    ...mapActions(['asyncAddCount']),
    asyncAdd(){
      this.$store.dispatch('asyncAddCount',233);
    }
  },
}
</script>
main.js
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    addCountNum(state, num) {
      state.count += num;
    },
  },
  actions: {
    // context 表示当前 store 的实例,可以通过 context.commit 来提交 mutations
    // 参数2:允许在使用 actions 的方法中向函数中传递参数
    asyncAddCount(context, num) {
      setTimeout(() => {
        context.commit('addCountNum', num);
      }, 3000);

    },
  },
});

效果展示

%title插图%num
getters

简介

getters 是 veux 实例中,充当计算属性的对象。除 state 之外,有时候我们还需要从 state 中派生出一些状态,
这些状态是依赖 state 的。此时会用到 getters

定义

通过 main.js 中 store 实例中的 getters 对象去定义;

main.js
const store = new Vuex.Store({
  state: {
    age: 19,
  },
  // getter 函数的第一个参数是 state
  // 必须要有返回值
  getters: {
    isAdult: state => state.age > 18 ? '成年' : '未成年',
  }
});

使用

vue组件通过 原始方式 和 辅助函数 来获取 getters 中的数据

原始方式 – $store.getters

App.vue
<template>
  <div id="app">
    <div>{{ $store.getters.filterList }}</div>
  </div>
</template>
main.js
const store = new Vuex.Store({
  state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  },
  // getter 函数的第一个参数是 state
  // 必须要有返回值
  getters: {
    filterList: state => state.list.filter(item =>  item > 5)
  }
});

效果展示

%title插图%num

辅助函数 – mapGetters(方法名)

App.vue
<template>
  <div id="app">
    <div>{{ filterList }}</div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
export default {
  computed: {
    ...mapGetters(['filterList'])
  }
}
</script>
main.js
const store = new Vuex.Store({
  state: {
    list: [1, 2, 3, 4, 5, 6, 7, 8, 9],
  },
  // getter 函数的第一个参数是 state
  // 必须要有返回值
  getters: {
    filterList: state => state.list.filter(item =>  item > 5)
  }
});

效果展示

%title插图%num
总结

通过以下表格展示关于 state、mutations、actions 之间的功能和调用方式

对象 描述 原始方式 辅助函数
state 存放vex的所有公共状态数据 $store.state.值 mapState()
mutations mutations 通过定义同步方法可去操作或修改 state 数据 this.$store.commit() mapMutations()
actions actions 是负责 vuex 中主要负责进行异步操作 this.$store.dispatch() mapActions()
getters getters 是 veux 实例中,充当计算属性的对象 this.$store.getters() mapGetters()
Vuex 模块化

模块化的概念设定是为了解决并提高项目开发的维护性

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。这使得 store 在后期开发中变得相当臃肿

%title插图%num
modules

用途

通过使用 modules 对象,使各个数据拆分,能针对性的进行分类的维护

%title插图%num

定义

将项目中所有大的逻辑和业务根据功能拆分成各个小块,通过 modules 中定义各个小块的子模块;实现业务的拆分

  • 定义两个模块 user 和 setting
  • user 中管理用户状态 token
  • setting 中管理应用的名称 name
main.js
const store = new Vuex.Store({
  modules: {
    user: {
      state: {
       token: 'smmcatmaxpro'
      },
      mutations: {},
      actions: {}
    },
    setting: {
      state: {
       name: '屎猫猫'
      },
      mutations: {},
      actions: {}
    }
  }
});

应用

方式一:通过 $store.state.模块名称.属性名 方式调用 modules 中创建的子模块的 state 成员

App.vue
<template>
  <div id="app">
    <ul>
      <li><b>token:</b>
        {{ $store.state.user.token }}
      </li>
      <li><b>姓名:</b>
        {{ $store.state.setting.name }}
      </li>
    </ul>
  </div>
</template>

效果展示

%title插图%num

方式二:通过 根级别的 getters 对象中设置 成员 并引用不同模块的 数据,达到快捷访问的效果

main.js
const store = new Vuex.Store({
  modules: {
    ...
  },
  getters: {
    token: state => state.user.token,
    name: state => state.setting.name,
  }
});
App.vue
<template>
  <div id="app">
    <ul>
      <li><b>token:</b>
        {{ token }}
      </li>
      <li><b>姓名:</b>
        {{ name }}
      </li>
    </ul>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
export default {
  computed: {
    ...mapGetters(['token', 'name']),
  }
}
</script>

效果展示

%title插图%num
命名空间

描述

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的。
这使得多个模块能对同一 mutation 或 action 做出响应

%title插图%num

因此,各个子模块都可以直接通过全局的方式调用。并无封闭性;

调用子模块方法

子模块定义的 mutation 方法允许通过 commit 全局直接调用执行

【不封闭性】模块化的 mutation 方法无论是使用 原始调用 和 辅助函数 方式都可以直接通过方法名直接调用,
这导致了模块之间毫无隐私,且缺乏高封闭性

main.js
const store = new Vuex.Store({
  modules: {
    user: {
      state: {
        token: 'smmcatmaxpro'
      },
      mutations: {
        updateToken(state) {
          state.token = 'maxsmmnacercat'
        }
      },
      actions: {}
    },
    setting: {
      ...
  },
  getters: {
    token: state => state.user.token,
    name: state => state.setting.name,
  }
});
App.vue
<template>
  <div id="app">
    <ul>
      <li><b>token:</b>
        {{ token }}
      </li>
      <li><b>姓名:</b>
        {{ name }}
      </li>
    </ul>
    <button @click="updateToken">改变token值</button>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';
export default {
  computed: {
    ...mapGetters(['token', 'name']),
  },
  methods:{
    updateToken(){
      this.$store.commit('updateToken');
    }
  }
}
</script>

效果展示

%title插图%num

命名空间加锁

使用 namespaced 属性设置为 true ;将保证内部模块的高封闭性。而不会全局直接调用

【高封闭性】模块的本质是希望互不干扰,各顾各的。通过命名空间 namespaced 像是给模块加锁的操作。
被设置 namespaced:true 的模块的内部数据将不允许被直接调用

设锁的模块中的功能被调用时,会抛出 “[vuex] unknown mutation type: updateToken” 错误。

main.js
modules: {
    user: {
      namespaced: true,
      state: {
        token: 'smmcatmaxpro'
      },
      mutations: {
        updateToken(state) {
          state.token = 'maxsmmnacercat'
        }
      },
      actions: {}
    }
});

访问带命名空间模块

方案1:直接调用 – 带上模块的属性名路径 例如:user/updateToken

Vue
<template>
  <div id="app">
    <ul>
      <div>{{ $store.state.user.token }}</div>
    </ul>
    <button @click="updateToken">改变token值</button>
  </div>
</template>

<script>
export default {
  methods:{
    updateToken(){
      this.$store.commit('user/updateToken');
    }
  }
}
</script>

效果展示

%title插图%num

方案2:辅助函数 – 带上模块的属性名路径 例如:user/updateToken,并嵌套在另一个方法中执行

【注意】从 mapMutations 调出的 user/updateToken 是以原本的名字为函数名。
这个名字作为函数名是不合法的。因此直接执行会报错!
通过导入后,嵌套在另一个方法中,通过 Vue实例挂载的 this 指向该函数并执行的方式可以实现 该函数的执行

Vue
<template>
  <div id="app">
    <ul>
      <div>{{ $store.state.user.token }}</div>
    </ul>
    <button @click="test">改变token值</button>
  </div>
</template>

<script>
import { mapMutations } from 'vuex';
export default {
  methods:{
    ...mapMutations(['user/updateToken']),
    test(){
      this['user/updateToken']();
    }
  }
}
</script>

效果展示

%title插图%num

方案3:使用 createNamespacedHelpers 创建基于某个命名空间辅助函数

【注意】与 vuex 引入的 mapMutations 存在命名冲突,注意需要手动更改名称
例如:const userMapmutations = createNamespacedHelpers(‘user’).mapMutations;

App.vue
<template>
  <div id="app">
    <ul>
      <div>{{ $store.state.user.token }}</div>
    </ul>
    <button @click="updateToken">改变token值</button>
  </div>
</template>

<script>
import { createNamespacedHelpers } from 'vuex';
const { mapMutations } = createNamespacedHelpers('user');
export default {
  methods: {
    ...mapMutations(['updateToken']),
  }
}
</script>

效果展示

%title插图%num