全面拥抱Vue3,Vuex4 最新详解教程!

举报
CoderBin 发表于 2022/10/04 08:43:14 2022/10/04
【摘要】 前言大家好,我是 CoderBin,本次将跟大家分享关于 Vuex4 的相关知识点和使用技巧,希望大家都能有收获,谢谢!创作不易,你们的点赞收藏留言就是我最大的动力💓如果文中有不对、疑惑的地方,欢迎在评论区留言指正🌻 Vuex 是什么? ☞官方文档官方说法:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保...

前言

大家好,我是 CoderBin,本次将跟大家分享关于 Vuex4 的相关知识点和使用技巧,希望大家都能有收获,谢谢!

创作不易,你们的点赞收藏留言就是我最大的动力💓

如果文中有不对、疑惑的地方,欢迎在评论区留言指正🌻

Vuex 是什么? ☞官方文档

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

Vuex4 的工作流程

1. 学习初始

这里使用 vuecli 来快速创建一个vue3项目,这里选择 JS 编写(使用 JS 或 TS 都可以)

vue create learn_vux

2. 安装

我们这里使用的是vuex4.x,安装的时候需要添加 next 指定版本

npm install vuex@next --save

3. 基本配置

3.1 创建仓库

在 src 目录下创建 store/index.js store 仓库

import { createStore } from "vuex";

const store = createStore({

})

export default store

3.2 在 main.js 文件中,有以下两个步骤:

import { createApp } from 'vue'
import App from './App.vue'

// 1. 导入 store
import store from './store'

// 2. 挂载到 Vue 根实例
createApp(App).use(store).mount('#app')

这样,一个 Vuex 仓库就构建完成啦!

4. state 选项的使用 state

全局共享的数据,只允许在 mutation 中修改(后面会讲)

  • state 提供唯一的公共数据源,所有共享的数据都要统一放到 Storestate 中进行存储
  • state 必须写成函数的形式并在 return 的对象中存放数据
import { createStore } from "vuex";

const store = createStore({
  // state 提供唯一的公共数据源
  state() {
    return {
      count: 10
    }
  }
})
export default store

4.1 在组件中的 template 和 script 中使用 state

  • 在 setup 中,必须通过实例 useStore() 才能拿到 store 中的数据
  • 通过 compute 获取 store 数据(缺点是数据只能一条一条取)
<template>
  <!-- 获取state -->
  <h2>{{ $store.state.count }}</h2>
  <h2>{{ ucount }}</h2>
</template>

<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
  name: 'App',
  setup() {
    // 实例 useStore(),获取 store 对象
    const store = useStore()
    // // 通过 compute 获取 store 数据
    const ucount = computed(() => store.state.count);

    return {
      ucount,
    }
  }
}
</script>

5. mapState 辅助函数

  1. 在组件中的 script 中使用 mapState,options API(Vue2) 的写法:
<template>
  <div>
    <h2>Home:{{ $store.state.counter }}</h2>
    <h2>Home:{{ sCounter }}</h2>
    <h2>Home:{{ sName }}</h2>
  </div>
</template>
<script>
// 使用 mapState 辅助函数
import { mapState } from 'vuex'
export default {
  // 也可以使用展开运算符和来原有的computed混合在一起;
  computed: {
    // 1. 数组写法
    // ...mapState(["counter", "name", "age", "height"])

    // 2. 对象写法(可以给state里面的值换个变量名)
    ...mapState({
      sCounter: state => state.counter,
      sName: state => state.name
    })
  }
}
</script>
  1. 在组件中的 script 中使用 mapState,composition API(Vue3) 的写法:
<template>
  <div>
    <h2>{{counter}}</h2>
    <h2>{{name}}</h2>
    <h2>{{age}}</h2>
    <h2>{{height}}</h2>
    <hr>
  </div>
</template>

<script>
import { mapState, useStore } from 'vuex'
import { computed } from 'vue'

export default {
  setup() {
    // 开始获取数据
    const storeStateFns = mapState(["counter", "name", "age", "height"])
    // {name: function, age: function, height: function}
    // 转化为:
    // {name: ref, age: ref, height: ref}
    const storeState = {}
    // 1. 取出 storeStateFns 中所有的 key,并遍历
    Object.keys(storeStateFns).forEach(fnKey => {
      // 2. 通过调用函数身上的 bind() 方法去绑定this,也就是store(属性为$store,值为store)
      const fn = storeStateFns[fnKey].bind({$store: store})
      // 3. 将 fn 函数转化为compute的形式赋值给每一个对应的key(其实就是store中state的变量,而函数则是每一个对应的值)
      storeState[fnKey] = computed(fn)
    })

    return {
      ...storeState
    }
  }
}
</script>

setup 中使用 mapState 会比较麻烦一点,现在来对这个方法进行封装:

在 src 目录下,创建 hook/useState.js 文件

import { computed } from 'vue'
import { mapState, useStore } from 'vuex'

export function useState(mapper) {
  // 拿到store对象
  const store = useStore()

  // 获取到对应的对象的functions: {name: function, age: function}
  const storeStateFns = mapState(mapper)

  // 对数据进行转换
  const storeState = {}
  Object.keys(storeStateFns).forEach(fnKey => {
    const fn = storeStateFns[fnKey].bind({$store: store})
    storeState[fnKey] = computed(fn)
  })

  return storeState
}

在组件中的 script 使用方式如下:

import { useState } from '../hooks/useState'

export default {
  setup() {
    // 数组、对象的写法都支持
    const storeState = useState(["counter", "name", "age", "height"])
    const storeState2 = useState({
      sCounter: state => state.counter,
      sName: state => state.name
    })

    return {
      ...storeState,
      ...storeState2
    }
  }
}

6. getters 选项的使用 getters

getters 可以对 Store 中已有的数据加工处理之后形成新的数据,类似 Vue 的计算属性。注意:getters 中只能返回用于展示的数据,不可修改数据

Store 中数据发生变化,getters 的数据也会跟着变化。

  • 参数一:state,可以获取到 state 里面的数据
  • 参数二:getters,可以使用其他 getters 的值
import { createStore } from "vuex";

const store = createStore({
  state() {
    return {
      count: 10
    }
  },
  // Store 中数据发生变化,getters 的数据也会跟着变化。
  // 参数一:state,可以获取到 state 里面的数据
  // 参数二:getters,可以使用其他 getters 的值
  getters: {
    doubleCount(state, getters) {
      return state.count * 2
    }
    // doubleCount:state => {
    //   return state.count * 2
    // }
  }
})
export default store

注意:可以采用箭头函数的写法、state 为上面的数据源、必须 return 出值

6.1 在组件中的 template 和 script 中使用 getters

<template>
  <!-- 获取getters -->
  <h2>doubleCount: {{ $store.getters.doubleCount}}</h2>
  <h2>doubleCount: {{dcount}}</h2>
</template>

<script>
import { computed } from 'vue';
import { useStore, mapState } from 'vuex'
export default {
  name: 'App',
  setup() {
    const store = useStore()
    // 获取getters
    const dcount = store.getters.doubleCount
    // 结合 computed 使用
    // const dcount = computed(() => store.getters.doubleCount)

    return {
      dcount
    }
  }
}
</script>

6.2 getters 的返回值可以是个函数

getters 的返回值是一个函数,可以接收参数,然后调用这个函数。一般用于需要接收参数的时候使用

import { createStore } from "vuex";

const store = createStore({
  state() {
    return {
      count: 10
    }
  },
  getters: {
    countN(state, getters) {
      // 调用时接收一个参数
      return function(n) {
        return state.count + n
      }
    }
  }
})
export default store

template 中使用时就要调用这个 return 的函数

<template>
  <h2>countN: {{$store.getters.countN(5)}}</h2>
</template>

7. mapGetters 辅助函数

  1. 在组件中的 script 中使用 mapGetters,options API 的写法:
<template>
  <div>
    <h2>{{ ageInfo }}</h2>
    <h2>{{ heightInfo }}</h2>
    <h2>{{ sNameInfo }}</h2>
    <h2>{{ sAgeInfo }}</h2>
  </div>
</template>

<script>
  import { mapGetters } from 'vuex'
  export default {
    // option API 写法
    computed: {
      // 数组写法
      ...mapGetters(["nameInfo", "ageInfo", "heightInfo"]),
      // 对象写法
      ...mapGetters({
        sNameInfo: "nameInfo",
        sAgeInfo: "ageInfo"
      })
    },
  }
</script>
  1. 在组件中的 script 中使用 mapGetters,composition API 的写法:
  • setup 中使用 mapGetters 跟使用 mapState 的方法差不多,这里也先封装成一个hook,然后直接调用。
    在 src 目录下,创建 hook/useGetters.js 文件
import { computed } from 'vue'
import { mapGetters, useStore } from 'vuex'

export function useGetters(mapper) {
  // 拿到store对象
  const store = useStore()

  // 获取到对应的对象的functions: {name: function, age: function}
  const storeStateFns = mapGetters(mapper)

  // 对数据进行转换
  const storeState = {}
  Object.keys(storeStateFns).forEach(fnKey => {
    const fn = storeStateFns[fnKey].bind({$store: store})
    storeState[fnKey] = computed(fn)
  })

  return storeState
}

在组件中的使用方式如下:

<template>
  <div>
    <h2>传统写法:{{ sNameInfo }}</h2>
    <h2>{{ nameInfo }}</h2>
    <h2>{{ ageInfo }}</h2>
    <h2>{{ heightInfo }}</h2>
    <hr>
  </div>
</template>

<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useGetters } from '../hooks/useGetters'

export default {
  setup() {
    // 传统写法
    const store = useStore();
    const sNameInfo = computed(() => store.getters.nameInfo);

    // 封装 getters 后的写法
    const storeGetters = useGetters(["nameInfo", "ageInfo", "heightInfo"])

    return {
      sNameInfo,
      ...storeGetters
    }
  }
}
</script>

8. mutations 选项的使用 mutations

一条重要的原则就是要记住 mutation 必须是同步函数

  1. Mutation 用于变更 Store中 的数据。
  2. 只能通过 mutation 变更 Store 数据,不可以直接操作 Store 中的数据。
  3. 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。

参数

  • 参数一:state,可以获取state中的数据
  • 参数二:载荷,即额外的参数
  • 在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
import { createStore } from "vuex";

const store = createStore({
  state() {
    return {
      count: 10
    }
  },
  getters: {
    doubleCount(state, getters) {
      return state.count * 2
    }
  },

  // 参数一:state,可以获取state中的数据
  // 参数二:载荷,即额外的参数
  // 在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
  mutations: {
    increment(state, payload) {
      state.count += payload.num
    }
  }
})
export default store

8.1 在组件中的 template 和 script 中提交 mutations

<template>
  <div>
    <hr>
      <button @click="$store.commit('increment')">+1</button>
      <button @click="addTen">+10</button>
    <hr>
  </div>
</template>

<script>
export default {
  methods: {
    addTen() {
      // 普通的写法
      // this.$store.commit('incrementN', {num: 10})
      
      // 另外一种提交风格
      this.$store.commit({
        type: 'increment_n',
        num: 10, 
      })
    }
  }
}
</script>

9. mapMutations 辅助函数

<template>
  <div>
    <h2>当前计数: {{ $store.state.counter }}</h2>
    <hr>
      <button @click="increment">+1</button>
      <button @click="add">+1</button>
    <hr>
  </div>
</template>

<script>
import { mapMutations, mapState } from 'vuex'

export default {
  // options API 中的使用
  methods: {
    ...mapMutations(["increment", ]),
    ...mapMutations({
      add: "increment"
    })
  },

  // composition API 中的使用
  setup() {
    const storeMutations = mapMutations(["increment",])

    return {
      ...storeMutations
    }
  }
}
</script>

10. actions 选项的使用 actions

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

注意

  1. actions 用于处理异步任务
  2. 在 actions 中,不能直接修改 state 中的数据
  3. 必须通过 context.commit() 触发某个 mutations 才行
import { createStore } from "vuex";

const store = createStore({
  state() {
    return {
      count: 10
    }
  },
  getters: {
    doubleCount(state, getters) {
      return state.count * 2
    }
  },
  mutations: {
    increment(state, payload) {
      state.count ++
    }
  },
  // 1. actions 用于处理异步任务
  // 2. 在 actions 中,不能直接修改 state 中的数据
  // 3. 必须通过 context.commit() 触发某个 mutations 才行
  actions: {
    incrementAction(context, payload) {
      console.log(payload)
      setTimeout(() => {
        context.commit('increment')
      }, 1000);
    },
  }
})
export default store

10.1 组件中使用,派发 actions

<template>
  <div>
      <button @click="increment">+1</button>
      <button @click="add">+1</button>
    <hr>
  </div>
</template>

<script>
import { useStore, mapState } from 'vuex'

export default {
  // composition API 的写法
  setup() {
    const store = useStore()
    const add = () => {
      store.dispatch('incrementAction', {count: 100})
    }
  }

  // options API 的写法
  methods: {
    increment() {
      // 携带参数
      this.$store.dispatch("incrementAction", {count: 100})
    },
    decrement() {
      // 3.派发风格(对象类型)
      this.$store.dispatch({
        type: "decrementAction"
      })
    }
  }
}
</script>

11. mapActions 辅助函数

<template>
  <div>
    <h2>当前计数: {{ $store.state.counter }}</h2>
    <hr>
      <button @click="incrementAction">+1</button>
      <button @click="decrementAction">-1</button>
      <button @click="add">+1</button>
      <button @click="sub">-1</button>
    <hr>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  // options API 写法
  methods: {
    // ...mapActions(["incrementAction", "decrementAction"]),
    // ...mapActions({
    //   add: "incrementAction",
    //   sub: "decrementAction"
    // })
  },

  // composition API 写法
  setup() {
    const actions = mapActions(["incrementAction", "decrementAction"])
    const actions2 = mapActions({
      add: "incrementAction",
      sub: "decrementAction"
    })

    return {
      ...actions,
      ...actions2
    }
  }
}
</script>

12. modules 选项的使用 modules

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

由于modules 内容太多且稍显复杂,这里不在演示,若想继续学习可前往官网学习


每文一句:非学无以广才,非志无以成学。

ok,关于 Vuex4 的简单使用就到这里,看到这里相信你对 Vuex4 已经有了简单的了解。如果想对 Vuex4 进行更加深入的理解,请移步 Vuex4中文文档 深入研究。有任何疑问都可以在评论区留言,大家一起研究,谢谢!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。