前端vuex

1、什么是vuex

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

我们是否可以通过组件数据的传递来完成呢?

  • 对于一些简单的状态,确实可以通过props的传递或者Provide的方式来共享状态;
  • 但是对于复杂的状态管理来说,显然单纯通过传递和共享的方式是不足以解决问题的,比如兄弟组件如何共享 数据呢

1.这个状态自管理应用包含以下几个部分:

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

image-20220327231827486

Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。

2. 使用Vuex 统一管理状态的好处

① 能够在 vuex 中集中管理共享的数据,易于开发和后期维护

② 能够高效地实现组件之间的数据共享,提高开发效率

③ 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步

什么样的数据适合存储到 Vuex 中

一般情况下,只有组件之间共享的数据,才有必要存储到 vuex 中;对于组件中的私有数据,依旧存储在组件

自身的 data 中即可。

Vuex和单纯的全局对象有什么区别呢?

第一:Vuex的状态存储是响应式的

当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会被更新;
第二:你不能直接改变store中的状态

改变store中的状态的唯一途径就显示提交 (commit) mutation;
这样使得我们可以方便的跟踪每一个状态的变化,从而让我们能够通过一些工具帮助我们更好的管理应用的状态;

3. 五个核心属性

(1)state

  • 可以理解为组件当中的data
  • 组件当中的data 是一个局部的数据仓库
  • state 是一个全局的数据仓库 只要数据存放到state之后 任何组件都能访问state里面的属性
  • 把组件当中需要被共享的属性存放到vuex当中

(2)mutations

  • mutations 可以理解为全局的方法 跟组件当中的methods类似

  • 定义在组件当中的methods是局部方法

  • 定义在mutations里面的方法 可以被任何组件所访问

  • 如果想要state当中的数据变化 需要在mutations里面修改 这是一个规范

  • 载荷
    通俗的理解就是传递的参数
    this.$store.commit(“upts”,{name:”赵六”,count:2});

  • 在vuex的mutations当中 传递的载荷只能传递一个
    如果想要传递多个载荷的化 可以把传递的参数合并成数组或者对象

(3)getters

  • 类似与computed
  • 用于监听vuex state数据的变化 用法跟计算属性的用法一致

(4)actions

  • 可以理解为全局的方法 跟组件当中的methods类似
  • mutations里面写的方法是一个同步方法
  • actions 里面写的方法 可以是同步 也可以是异步
  • 通过actions 解决了 mutations异步操作导致的 调试工具与页面所展示的数据不一致的问题
  • 把异步操作写在actions 当中 在actions里面执行mutations里面的方法

(5)modules

模块或者分组,如果项目很大,要vuex管理的状态很多,都写到一个文件有点臃肿,这个时候分不同的模块,单个文件就非常小,而且单一集中更好管理。一般情况下都写在index.js中就够了。

2.使用vuex

在vue3.x中使用vuex的方法与vue2.x中使用vuex的方法是不同的

1.安装以及初始化

在vue3中使用vuex的版本要在4版本以上

npm install vuex@next
or
cnpm install vuex@4

vuex4 初始化方式做了相应的变化,使用新的 createStore 函数创建新的 store 实例。

//vuex全局管理文件
import { createStore } from "vuex";

const store = createStore({
  state: {
    count: 100,
    movie: '',
    price: "45元",
    str: '我是共享数据',
    count: 10,
    movie01: '花束般的恋爱',
    movie02: '狙击手',
    movie03: '奇迹笨小孩',
    movie04: '我们的冬奥',
    movie05: {
      title: '这个杀手不太冷静',
      price:50
    },
    num: 0,
    list: [],
    books:['西游记','水浒传','三国演义','人性的弱点','你当像鸟飞向那座山',"晚熟的人","人类简史","今日简史","未来简史","三体"],
  },

  mutations: {

  },
  actions: {

  },
  getters: {

  }

})
export default store;

//main.js
//store的注册
//直接在main.js里面使用app.use()进行安装即可。
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router"
import store from "./store"
createApp(App).use(router).use(store).mount('#app');

2.state的使用

<template lang="">
  <div>
    <h3>获取state数据</h3>
    <p>1.直接在页面上获取:{{$store.state.count}}</p>
    <p>2.普通方式获取state数据---{{getCount}}---{{movie01}}</p>
    <p>3.mapState获取state数据:{{count}}---{{movie02}}---{{movie03}}</p>

  </div>
</template>
<script setup>
  import { computed} from "vue"
  import {useStore,mapState } from "vuex"; 

  const store=useStore();
  //普通的方式获取state
  const getCount=computed(()=>store.state.count);
  const movie01=computed(()=>store.state.movie01);

//映射函数的方式获取state
const state={}
const fnState=mapState(["count","movie02","movie03"]);
Object.keys(fnState).forEach(fnKey=>{
  const fn=fnState[fnKey].bind({$store:store})
  state[fnKey]=computed(fn)

})
  const {count,movie02,movie03}=state;

</script>

默认情况下,Vuex并没有提供非常方便的使用mapState的方式,这里我们进行了一个函数的封装:

上面那种在setup函数里面写一大坨代码不是我们想要的效果。所以我们进行简单的函数封装

我们可以把映射函数mapState封装一下,使用会更加的方便:

先新建一个useState.js文件:

import { mapState, useStore } from "vuex";
import { computed } from "vue";

export const useMapState = (getKeys) => { 
  const store = useStore();
  const storeState = {};
  const storeFns = mapState(getKeys);
  Object.keys(storeFns).forEach((fnkeys) => { 
    const fn = storeFns[fnkeys].bind({ $store: store });
    storeState[fnkeys] = computed(fn);

  })
  return storeState;
}

bind方法的作用,就可以把相关的语法糖绑定,最终返回一个动态的ref或者reactive.

然后在组件中引入:

import {useMapState} from "../utils/useState"
<script setup>

const {count,accessToken,rolename}=useMapState(["count","accessToken","rolename"])
</script>

不在setup语法糖中的使用

<script>
import { useMapState } from "@/utils/useState"
export default {
  setup(props) {
    const states=useMapState(["count","movie01","movie02"]);

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

3.mutations的使用

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

<template>
  <div>
    <h3>使用mutations</h3>
    <p> 1.普通方式提交mutations:<button @click="add">add count--{{count}}</button> </p>
    <p>2.映射函数mapMutations: <button @click="getCount(100)">getCount --{{count}}</button> </p>
  </div>
</template>
<script setup>
  import { useStore,mapMutations } from 'vuex';
 import { computed } from 'vue';
const store=useStore();
 const count=computed(()=>store.state.count);
const add=()=>{
  store.commit("addCount",10);
}
const getCount=mapMutations(["addCount"]).addCount.bind({$store:store});

</script>

如果不是在setup语法糖中:mapMutations返回的对象里面的属性值就刚好是函数,不需要再次封装,直接解构即可使用

export default {
   setup(props) {
     const storeMutations=mapMutations({
           getCount:'addCount',
           getMovie:"getMovie"
     })
    return {
      ...storeMutations
    }
   }
 }

4.actions

Action类似于mutation,不同在于:

Action提交的是mutation,而不是直接变更状态;

Action可以包含任意异步操作;

这里有一个非常重要的参数context:

context是一个和store实例均有相同方法和属性的context对象;
所以我们可以从其中获取到commit方法来提交一个mutation,或者通过 context.state 和 context.getters 来 获取 state 和 getters;
但是为什么它不是store对象呢?这个等到我们讲Modules时再具体来说;

<template>
  <div>
    <h3>分发actions</h3>
   <p>1.普通方式分发actions: <button @click="getList">分发actions</button>  </p>
   <p>2.映射方式分发actions: <button @click="getMovieList">分发actions</button>  </p>
  </div>
</template>
<script>
import { useStore,mapActions} from "vuex"
export default {
  setup(props) {
    var store=useStore();
    const  getList=()=>{
      store.dispatch("asyncGetList");
    }
    const storeActions=mapActions({
      getMovieList:"asyncGetList"
    })
    return {
      getList,
      ...storeActions
    }
  }
}
</script>

在setup语法糖中的用法:

<script setup>
import { useStore,mapActions} from "vuex"
var store=useStore();
 const  getList=()=>{
      store.dispatch("asyncGetList");
    }
 const getMovieList=mapActions(["asyncGetList"]).asyncGetList.bind({$store:store});
</script>

5.getters用法

某些属性我们可能需要经过变化后来使用,这个时候可以使用getters:

<template>
  <div>
    <h3>getters</h3>
    <p>1.普通的方式获取getters:-----{{disCurrent}}</p>
    <p>2.映射函数的方式获取getters:---{{currentDiscount}}---{{total}}</p>
  </div>
</template>
<script setup>
import { useStore,mapGetters } from 'vuex';
import { computed } from 'vue';
//引入封装好的映射函数
import {useMapGetters} from "../utils/useGetters"
const store=useStore();
//1.普通方式获取getters
//获取当前的折扣
const disCurrent=computed(()=>store.getters.currentDiscount);

//2.映射函数的方式获取getters
/** 
const getters={};
const storeGetters=mapGetters(["currentDiscount","total"]);
Object.keys(storeGetters).forEach(fnkey=>{
  getters[fnkey]=computed(storeGetters[fnkey].bind({$store:store}))
})

const {currentDiscount,total}=getters;
*/
//使用封装好的方法
const {currentDiscount,total} =useMapGetters(["currentDiscount","total"]);
</script>
<style lang="scss">

</style>

在Vue3中,我们常常在Composition Api中使用到vuex的mapState和mapGetters,因为每次获取很麻烦所以就封装了他们,支持模块命名,一键使用,轻松上手

主要实现函数:useMapper.js

import { computed } from "vue"
import { useStore } from "vuex"
// 组合mapState和mapGetters
export default function(data, mapFn) {
    const store = useStore()
    const storeGettersFns = mapFn(data)
    const storeGetters = {}
    Object.keys(storeGettersFns).forEach( fnkey => {
        const fn = storeGettersFns[fnkey].bind({$store: store})
        storeGetters[fnkey] = computed(fn)
    }) 
    return storeGetters
}

两个供调用的函数: useState.js 和 useGetters.js
useGetters.js

import { mapGetters, createNamespacedHelpers } from "vuex"
import useMapper from "./useMapper"

export default function (name, getters) {
    let mapFn = mapGetters
    if (typeof name === 'string' && name.length > 0) {
        mapFn = createNamespacedHelpers(name).mapGetters
    }
    return useMapper(getters, mapFn)
}

useState.js

import { mapState, createNamespacedHelpers } from "vuex"
import useMapper from "./useMapper"

export default function (name, states) {
    let mapFn = mapState
    if(typeof name === 'string' && name.length > 0 ) {
        mapFn = createNamespacedHelpers(name).mapState
    }
    return useMapper(states, mapFn)
}

使用

写法一:

import useState from "../../hooks/useMapState"
import useGetters from "../../hooks/useGetters"
export default {
    setup(){
        const mapStoreState = useState(['name', 'age', 'gender'])
        const mapStoreGetters = useGetters(['fullName'])

        return {
            ...mapStoreState,
            ...mapStoreGetters
        }
    },
}

写法二: 如果store使用了模块定义的,用这种写法

import useState from "../../hooks/useMapState"
import useGetters from "../../hooks/useGetters"
export default {
    setup(){

        const mapStoreState = useState('user', ['name', 'age', 'gender'])
        const mapStoreGetters = useGetters('user', ['fullName'])

        return {
            ...mapStoreState,
            ...mapStoreGetters
        }
    },
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值