Vuex的使用以及持久化的实现(2.0版本)
什么是 vuex ?这里不进行过多介绍,请直接看官网
Vuex 主要有四部分:
1. state: 包含了 store 中存储的各个状态。
2. getter:类似于 Vue 中的计算属性,根据其他 getter 或 state 计算返回值。
3. mutation:一组方法,是改变store
中状态的执行者,只能是同步操作。
4. action:一组方法,其中可以包含异步操作。
源码代码在 文章的最下方(注意: 首次运行本项目时,请先输入命令: npm install ,进行安装对应的模块依赖)
1、假设你的项目已经建好,并且也已经安装了 Vuex ....
2、在 src 目录中 新增一个名为 store 的文件夹,我的项目结构如下:
3、在 store 文件夹中 新增 index.js 文件
内容如下 ( require.context('./modules') 这个 modules 怎么来的 在 第4个步骤 有说明 )
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // 批量自动导入 自定义不同的vuex模块 let modules = {} // 在 modules当前目录中 找出所有以 .js 结尾的文件 const files = require.context('./modules', false, /\.js$/); files.keys().forEach(key => { modules[key.replace(/(\.\/|\.js)/g, "")] = files(key).default; }) Object.keys(modules).forEach((key) => { // 使其成为带命名空间的模块。 // 保证在变量名一样的时候,添加一个父级名拼接 modules[key]["namespaced"] = true; }); // 以下的结果为: /** * * { * "storeDemo1": { 第一个模块 * "namespaced": true, * "state": { * "aFlag": false * }, * }, * "storeDemoN.." : { 第 n 个 模块 * "namespaced": true, * "state": { * "N..Flag": false * }, * } * } */ const store = new Vuex.Store({ modules }) export default store;
4、在 store 文件夹中 新增 modules 文件夹,然后再 modules 文件夹 新建一个 storeDemo1.js 文件
storeDemo1.js 内容如下
/** * 数据源 */ const state = { aFlag: false, msg: 'oukele', } /** * 在mutations中写上自定义的方法, * 然后在组件的js中通过 this.$store.commit("自定义的方法名") * 可以更新 store(即上方定义的 state对象 的属性值) 中的数据和状态 * * 注意:mutations 必须是同步函数 ( 因为在 mutations 中导致任何数据源状态变更都应该在此刻完成 ) * 官方解释:在 mutation 中混合异步调用会导致你的程序很难调试。 * 例如,当你调用了两个包含异步回调的 mutation 来改变状态, * 你怎么知道什么时候回调和哪个先回调呢?这就是为什么我们要区分这两个概念。 * 在 Vuex 中,mutation 都是同步事务 * * 为了处理异步操作,请使用 Action */ const mutations = { /** * 更改 state 中 aFlag 的值 方法 * @param state 数据源对象 */ changeAFlag(state){ state.aFlag = true; }, /** * 更改 state 中 msg 的值 方法( 可以传递自定义参数 ) + 本地持久化 * @param state 数据源对象 * @param params 传递的自定义参数 * @param isEndurance 是否本地持久化 */ changeMsg(state , params , isEndurance){ // DOTO // 等会再完善 console.log( state , params + " - " + isEndurance ) } } /** * store 中定义“getter”(可以认为是 store 的计算属性) * Getter 会暴露为 store.getters 对象,你可以以属性的形式访问 state(数据源中的) 这些值 */ const getters = { /** * 在外部中 可通过 this.$store.getters.getAFlag 取出 aFlag 的值 * 注意:因为我这里使用了 模块化,所以使用时应该是这样的调用: * 模块名 + 调用的函数 * this.$store.getters['storeDemo1/getAFlag'] * * @param state */ getAFlag(state){ return state.aFlag; }, msg( state ){ return state.msg; } } export default { state , mutations , getters }
5、上面的 步骤 都是为 Vue实例提供创建好的store ,因此使用时,需在 main.js 进行 注入该 store
6、因为我是使用 vue/cli 创建的项目,创建出来的项目有一个简单的示例,我这边去除部分冗余代码
App.vue 内容如下
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Hello World"/> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { HelloWorld } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
HelloWorld.vue 内容如下
<template> <div class="hello"> <h1>{{ msg }}</h1> <div class="demo-body"> <div class="showText"> <div> aFlag:{{ aFlag2 }} </div> <div> msg: {{ vuexMsg }} </div> </div> <div class="operating"> <button @click="changeAFlag"> 操作 aFlag </button> <button @click="changeMsg(false)"> 操作 msg </button> <button @click="changeMsg(true)"> 操作 msg + 持久化 </button> </div> </div> </div> </template> <script> import {mapState , mapGetters} from "vuex"; export default { name: 'HelloWorld', props: { msg: String }, computed: { // 获取 vuex 的值 有以下几种方式 // 1. aFlag0(){ return this.$store.getters['storeDemo1/getAFlag']; }, // 2. 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters("storeDemo1",{ aFlag1: 'getAFlag' }), // 也可以这样 ...mapGetters({ aFlag2: 'storeDemo1/getAFlag', vuexMsg: 'storeDemo1/msg', // 取出 某个 模块中的 getAFlag 函数返回的值,并且映射到 nFlag 上 // nFlag: 'xxx/getAFlag', }), // 3. ...mapState("storeDemo1",{ aFlag3:state => state.aFlag }), }, methods: { /** * 修改存在 vuex 中的aFlag值 */ changeAFlag(){ this.$store.commit('storeDemo1/changeAFlag'); }, changeMsg(isEndurance){ console.log(isEndurance) this.$store.commit('storeDemo1/changeMsg'); } } } </script> <style scoped> .demo-body { width: 30%; margin: auto; display: flex; flex-direction: column; } .demo-body .showText { background-color: blanchedalmond; padding-top: 10px; height: 150px; } .demo-body .operating { padding: 10px 0 10px 0; background-color: gray; } </style>
7、将项目运行起来效果如下
8、当点击 操作aFlag 按钮时,会将 aFlag的值修改成 true
操作aFlag 按钮 绑定上了
changeAFlag 函数
changeAFlag 函数内容如下 ( 注意:mutations 中的方法,要使用 store.commit 方法来触发 )
为什么是 this.$store.commit('storeDemo1/chageAFlag') ,而不是 this.$store.commit('chageAFlag')?
因为 我们这个项目 是将 store 分割成 模块,每个模块拥有自已的 state,mutation,action,getter
直接 使用 this.$store.commit('chageAFlag') ,浏览器会提示:它不知道你要执行哪一个模块的 chageAFlag 的方法
storeDemo1 模块中 的 changeAFlag 方法
aFlag的值 由 false 变成 true ,说明 我们将 vuex 中的 数据修改成功了。
但是 由于 vuex的数据 是存在内存中的,只要一经过刷新,所有的数据都会丢失,有时候有些数据我们并不想那么轻易让它就这丢失了,比如 用户的token
所以 下面的步骤 将采取 vuex + localStore 的方式实现 vuex的持久化效果
9、实现 vuex 的持久化效果
完善 storeDemo1.js 中 mutations 的 chageMsg 方法代码
注意:我这里 的 isEndurance 是无法获取外部传递过来的值(....尴尬了..)
官方解释:
完善的内容如下:
完善 storeDemo1.js 中 getter 的 msg 方法代码
完善 HelloWorld.vue 中 操作 msg 和 操作 msg + 持久化 按钮 触发事件
测试效果如下:
10、Vuex 的 action 使用
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
在 HelloWorld.vue 新增一个按钮 + 对应的事件 + 新增一个显示的状态
新增一个显示的状态
按钮的事件
storeDemo1.js 新增 actions 组方法
效果如下:
GitHub地址:vueProject/vuex-demo at main · oukele/vueProject (github.com)