likes
comments
collection
share

vuex你还记得多少?

作者站长头像
站长
· 阅读数 10

前言

Vuex是专为Vue.js应用程序开发的状态管理模式。它解决了组件之间同一状态的共享问题,采用了集中式存储管理应用的所有组件状态,从而使组件可以和store通讯。在Vuex中,有mutationsactions类似Redux中的reducer,用来更改状态或执行异步操作。 vuex是单向数据流,以下大概是vuex的运行机制:

vuex你还记得多少?

vuex的回顾

首先我们先安装vuex

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

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

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属性内来定义状态

index.js

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

访问vuex的状态

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内状态

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的一些初始状态处理后的状态,可以说是计算属性,但不会被缓存

vuex你还记得多少?

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是一样的

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)

app.vue

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

mutation

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

index.js

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

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

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中

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 可以包含任意异步操作。
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来进行调用

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

app.vue

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

module

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

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

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]这样才能拿到值,所以这里直接推荐使用辅助函数,这样更加简洁

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方法来辅助命名空间函数的使用

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本身

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)

转载自:https://juejin.cn/post/7278245966598684713
评论
请登录