vuex你还记得多少?

前言

Vuex是专为Vue.js应用程序开发的状态管理模式。它解决了组件之间同一状态的共享问题,采用了集中式存储管理应用的所有组件状态,从而使组件可以和store通讯。在Vuex中,有mutations、actions类似Redux中的reducer,用来更改状态或执行异步操作。

vuex是单向数据流,以下大概是vuex的运行机制:

vuex的回顾

首先我们先安装vuex

js 复制代码
//如果需要指定版本 可以npm i vuex@3.xx 等  现在vuex默认版本为4.0
npm i vuex

紧接着要创建vuex,在项目的src目录下创建store文件夹,紧接着再创建index文件(当然也可以随便找个地方创建store文件,但主要都墨守成规了,这样更符合规范)

js 复制代码
index.js

//从vuex中引入
import { createStore } from "vuex";
const store = createStore();
//创建好之后 默认导出
export default store;

main.js

import { createApp } from "vue";
import App from "./App.vue";
import store from "./store";//从刚才创建的文件夹内导入
createApp(App).use(store).mount("#app");//再vue实例上注册一下

这样简单的几步完成之后,就可以在项目的任意文件内访问vuex里的状态了

vuex的属性

vuex内部也是比较简单的,一共就五个属性state,getters,mutations,actions和modules,接下来分别回顾一下这些属性的作用

state

vuex中必须在state属性内来定义状态

js 复制代码
index.js

//这样我们就简单的创建的一个公共状态,这个状态可以被所有的vue文件访问到
const store = createStore({
  state: {
    name: "iceCode",
  },
});

访问vuex的状态

js 复制代码
app.vue

<template>
  <div>
    <h1>{{ name }}</h1>
    <!-- 或者这样使用,都是可行的 -->
    <h1>{{ $store.state.name }}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: this.$store.state.name,//赋值到data里再访问
    };
  },
};
</script>

当然还有另一种访问方式,vuex提供了辅助函数(mapState)让我们访问vuex内状态

js 复制代码
app.js

//首先是对象形式 访问时,就跟调用计算属性一样
 computed: mapState({
    //这里就是将vuex内name属性赋值给newName,访问时调用计算属性中的newName即可
    newName: (state) => state.name,
    //这里一样是重新赋值,name等价于state => state.name
    newTowName: "name",
    //如果需要配合vue文件内的状态使用也可以这样使用,与第一种类似
    nameFn(state) {
      return state.name + "手机号:" + this.phone;
    },
  }),
  
  //第二种时数组形式,更为简单,但不能重命名
  computed: {
    /* 
    这里直接就可以取到vuex中的name 计算属性中肯定不止vuex,还有一些自定义的
    所以利用展开运算符使vuex中的状态放入到计算属性中,调用还是一样的
    */
    //...
    ...mapState(['name'])
  }

getter

getters属性是对state的一些初始状态处理后的状态,可以说是计算属性,但不会被缓存

js 复制代码
index.js

const store = createStore({
  //...
  getters: {
    //有两个参数state状态,和其他getter
    newName(state, getter) {
      return state.name.splic(3, 0, "d");
    },
    nameLength(state, getter) {
      return state.name.length + getter.newName.length;
    },
  },
});

访问getters也是跟state是一样的

js 复制代码
app.vue

<template>
  <div>
    <h1>{{ nName }}</h1>
    <!-- 或者这样 -->
    <h1>{{ $store.getters.newName }}</h1>
    <h1>{{ $store.getters.nameLength }}</h1>
  </div>
</template>

<script>
export default {
//...
  computed: {
    nName: () => this.$store.getters.newName,
  },
};

同样的他也有一个辅助函数(mapGetters)

js 复制代码
app.vue

//同state一样有两种写法,也可以一样的解构
export default {
//...
  computed: {
    //...
    ...mapGetters( [ 'newName', 'nameLength' ] )
    ...mapGetters({
      name: "newName",
    }),
  },
};

mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler) 。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,第二个参数就是提交mutation时所携带的值

js 复制代码
index.js

const store = createStore({
  state: {
    name: "iceCode",
  },
  //再mutation中定义改变状态的方法
  mutations: {
    setName(state, value) {
      state.name = value;
    },
  },
});

在vue文件中,调用这个方法需要使用commit

js 复制代码
app.vue

<template>
  <div>
    <button @click="set">改变名字</button>
     <!-- 点击按钮之后,这里就改变为了'张三' -->
    <h1>{{ newName }}</h1>
  </div>
</template>

<script>
export default {
 //...
  computed: {
    newName() {
      return this.$store.state.name;
    },
  },
  methods: {
  //当点击按钮时,就会触发commit 调用vuex里的setNmae方法,将name的状态改变为'张三'
    set() {
      this.$store.commit("setName", "张三");
    },
  },
};

这里的辅助函数为mapMutations,但是它不再存放在计算属性中了,而是放在methods中

js 复制代码
app.vue

<template>
  <div>
      <!-- 使用这三种都方法调用效果都是一样的 -->
    <button @click="set">改变名字</button>
    <button @click="setName('李四')">改变名字1</button>
    <button @click="setMonicker('王五')">改变名字2</button>
    <h1>{{ newName }}</h1>
  </div>
</template>

<script>
import { mapMutations } from "vuex";
export default {
//...
  methods: {
    set() {
      this.$store.commit("setName", "张三");
    },
    //两个二选一,不能同时使用
    ...mapMutations( [ 'setName' ] )
    //或者
    ...mapMutations({
      setMonicker: "setName",
    }),
  },
};
</script>

重要

一条重要的原则就是要记住 mutation 必须是同步函数。想象一下,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用------实质上任何在回调函数中进行的状态的改变都是不可追踪的。

action

action就是异步版的mutation

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
js 复制代码
index.js

const store = createStore({
  state: {
    name: "iceCode",
  },
  mutations: {
    setName(state, value) {
      state.name = value;
    },
  },
  actions: {
    //context与vuex实例具有相同的方法,从中可以调用commit等,第二个参数value接收传进来的参数
    getDataAndSetName(context, value) {
      // 也可以试着return出来一个promist对象
      return new Promise((resolve, reject) => {
        //假设这里是一个异步请求 当请求完毕之后会调用commit
        setTimeout(() => {
          context.commit("setName", value);
          resolve(context.state.name);
        }, 1000);
      });
    },
    //既然context有着vuex实例相同的方法 在这里也可以将这些方法解构出来使用
    async logName({ commit, dispatch }) {
      //这里getDataAndSetName return出来一个promis对象,自然是可以使用.then等功能
      dispatch("getDataAndSetName").then((res) => {
        console.log(res);
      });
      //或者也可以使用async await
      const stateName = await dispatch("getDataAndSetName");
      console.log(stateName);
    },
  },
});

使用actions的方法需要dispatch来进行调用

js 复制代码
app.vue

<template>
  <div>
   <!-- 点击按钮,一秒后newName会改为 '张三' -->
    <button @click="set">改变名字</button>
    <h1>{{ newName }}</h1>
  </div>
</template>

<script>
export default {
  //...
  computed: {
    newName() {
      return this.$store.state.name;
    },
  },
  methods: {
    set() {
    //因为导出的为promis对象,在调用的时候也可以直接.then一个方法
      this.$store.dispatch("getDataAndSetName").then((res) => {
        console.log(res);
      });
    },
  },
};
</script>

使用辅助函数mapActions

js 复制代码
app.vue

//这里辅助函数的使用就跟mutation是一样的了,使用方法也完全一样
import { mapActions } from "vuex";
export default {
 //...
  methods: {
    //...
    ...mapActions( [ 'getDataAndSetName' ] )
    //或者
    ...mapActions( {
      getName:'getDataAndSetName'
    })
  },
};

module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块------从上至下进行同样方式的分割

js 复制代码
index.js

const store = createStore({
 //...
  modules: {
    person: {
    //命名空间,如果不写就是默认false,这样模块里的状态和跟状态会进行冲突
      namespaced: true,
      state: {},
      getters: {},
      mutations: {},
      actions: {},
      //在模块下还可以继续写模块
      modules: {
        man: {
          namespaced: true,
          state: {
          list:[1]
          },
          //...
        },
      },
    },
  },
});

当要访问模块里的状态就稍微繁琐一些,如果模块层次比较深,拿state为例,访问的时候需要this.$store.state.person.man.list[0]这样才能拿到值,所以这里直接推荐使用辅助函数,这样更加简洁

js 复制代码
app.vue
//如果是模块层次较深,大量使用辅助函数有助于代码的可读性
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {
 //...
  computed: {
    ...mapState("person/man", {
      lists: (state) => state.list,
    }),
    ...mapGetters("person/man", ["newList"]),
  },
  methods: {
    ...mapMutations( 'person/man', {
      addList:'setList'
    } )
    ...mapActions('person/man',['listApi'])
  },
};

另外为了更加方便,vuex还提供了一个createNamespacedHelpers方法来辅助命名空间函数的使用

js 复制代码
app.vue

<script>
//引入辅助函数
import {  createNamespacedHelpers } from "vuex";
const {mapState, mapGetters, mapMutations, mapActions}=createNamespacedHelpers('person/man')
export default {
 //...
 //这里就跟使用根节点的状态方法一样,感觉更简洁了
  computed: {
    ...mapState({
      lists: (state) => state.list,
    }),
    ...mapGetters(["newList"]),
  },
  methods: {
    ...mapMutations({
      addList:'setList'
    } )
    ...mapActions(['listApi'])
  },
};
</script>

在vue3的组合式中使用

vuex向外提供了一个useStore的hook,它的实例就是vuex本身

js 复制代码
app.vue

<script setup>
import {useStore} from 'vuex'
const store=useStore()
const name=computed(()=>store.state.name) 
store.commit('setName','iceCode')
store.dispatch('getName')
</script>

结尾

Vuex能够利用Vue.js的细粒度数据响应机制来进行高效的状态更新。它在Vue调试工具中集成了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。

在使用Vue构建复杂的大型应用时,Vuex可以使开发工作变得更为轻松便捷,它可以帮助开发者更好地组织和管理应用的状态,提高代码的可读性和可维护性。

如果是在vue3中还是建议使用pinia,也是官方比较推荐的:Home | Pinia 中文文档 (web3doc.top)

相关推荐
卸任15 小时前
Vibe coding工具使用心得
前端·ai编程
brien916 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance16 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M16 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写16 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李16 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易16 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding16 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马16 小时前
前沿视界
前端
kyriewen16 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员