vuex3详解及与vuex4的差异

Vuex是什么?

Vuex是一个专为 Vue.js应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信。

要点:

  1. vue官方搭配,专属使用 (类似于:vue-router),有专门的调试工具
  2. 集中式管理数据状态方案 (操作更简洁)data() { return { 数据, 状态 }}
  3. 数据变化是可预测的 (响应式)

使用Vuex的好处:

1、数据的存取一步到位,不需要层层传递

2、数据的流动非常清晰

3、存储在Vuex中的数据都是响应式的

那么我们先来思考一个问题:什么样的数据适合存储到Vuex中?

答案是:需要共享的数据

Vuex的作用就是:频繁、大范围的数据共享

vue官方提供的独立于组件体系之外的,管理公共数据的工具:

vuex 五个核心概念:

state: 统一定义公共数据(类似于data(){return {a:1, b:2,xxxxxx}})
mutations : 使用它来修改数据(类似于methods)
getters: 类似于computed(计算属性,对现有的状态进行计算得到新的数据)
actions: 发起异步请求
modules: 模块拆分

 

Vuex-state定义公共数据并在组件中使用:

概念:State 本质上就是 Object 对象

state的作用是:保存公共数据(多组件中共用的数据)

state是响应式的: 如果修改了数据,相应的在视图上的值也会变化。

项目结构:

需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cinema.js       # cinema模块
        └── tabbar.js   # tabbar模块
store基本创建:

vue2对应关系:2 ===>3 ===> 3
vue版本:^2.6.14
vue-router:“^3.5.2”
vuex版本:3.6.2

vue3对应关系:
vue3 ===> vue-router4 ===> vuex4

npm 指定版本号下载

npm install vuex@3.4.0 --save 安装并写入package.json的dependencies中
npm install vuex@3.4.0 -save-dev 安装并写入package.json的devDependencies中
npm uninstall xxx 删除 xxx 模块
npm uninstall -g xxx 删除全局模块 xxx

1)在store/index.js 中放置具体的代码,具体如下:

import Vue from 'vue'
import Vuex from 'vuex'
​
Vue.use(Vuex)
​
const store = new Vuex.Store({
  state: {
    // 定义共享状态
  },
  mutations: {
    // 定义同步修改状态的方法
  },
  actions: {
    // 定义异步修改状态的方法
  },
  getters: {
    // 定义计算派生状态的方法
  },
modules: {
    模块名1: {
            // namespaced为true,则在使用mutations时,就必须要加上模块名
            namespaced: true, 
            state: {},
            getters: {},
            mutations: {},
            actions: {},
            modules: {}
    },
    模块名2: {
        // namespaced不写,默认为false,则在使用mutations时,不需要加模块名
          state: {},
          getters: {},
          mutations: {},
          actions: {},
          modules: {}
    }  
  }



})
export default store

访问数据和修改数据的调整
访问模块中的数据,要加上模块名
获取数据项: {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}
访问模块中的mutations/actions:
如果namespaced为true,则需要额外去补充模块名
如果namespaced为false,则不需要额外补充模块名
$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true

使用了modules之后,在访问数据时就要额外添加modules的名字了。

建议在使用modules时,建议都给加上namespaced!

(1) state使用  辅助函数:mapState
// 方法一(直接使用)
this.$store.state.变量名

// 方法二(引入,之后可视为计算属性使用)
// 基于 Vuex 提供的 mapState 辅助函数,可以方便的把 Store 中指定的数据,映射为当前组件的计算属性
// 使用之前需要引入mapState,方法: import { mapState } from 'vuex';
computed:{
    ...mapState({在当前组件中使用的变量名: state => state.vuex仓库中的对应变量名}),
    ...mapState({'在当前组件中使用的变量名':'vuex仓库中的变量名'}),  //一次只能声明一个变量
    ...mapState({在当前组件中使用的变量名:state = state.vuex仓库中的对应变量名})
    ...mapState(['变量名1','变量名2']),
}
  (2) mutations使用  辅助函数:mapMutations

 特点:想要修改State 中的数据,只能调用Mutation 方法,它是Vuex中用来修改公共数据的唯一入口。

好处:能够确保修改来源的唯一性,方便调试和后期维护。

在定义时:它的第一个参数是state,第二个参数是载荷

在调用时:用 this.$store.commit('mutation名', 载荷) 来调用

// 方法一(直接使用)
this.$store.commit('mutation名', 实参);

// 方法二(引入,之后可视为methods方法使用)
// 基于 Vuex 提供的 mapMutations 辅助函数,可以方便的把 Store 中指定的方法,映射为当前组件的 methods
// 使用之前需要引入mapMutations,方法: import { mapMutations } from 'vuex';
methods:{
    ...mapMutations(['mutation名1','mutation名2']), 
    ...mapMutations({'在当前组件中使用的名字': 'mutation名'}),
    
     使用方法fun() {
       this.mutation名1();
    }
}

Vuex-mutaions拓展理解
问:为啥是$store.commit('mutations的名字')而不是$store.mutations的名字()?

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

问:数据不可以该在组件内部直接修改吗?

答:不能。虽然语法上不报错,也有响应式的特点。但是不推荐。特别是在严格模式下会报错。若将vue创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改state的操作,只要不经过 mutation的函数,vue就会报错。

(3) getter使用  辅助函数:mapGetters
// 方法一(直接使用)
this.$store.getters.getter的名字

// 方法二(引入,之后可视为计算属性使用)
// 基于 mapGetters 辅助函数,可以把 store 中的 getter 映射为当前组件的计算属性
// 使用之前需要引入mapGetters,方法: import { mapGetters } from 'vuex';
computed: { 
  ...mapGetters(['getterName1','getterName2']), 
  ...mapGetters({aliasName1: 'getterName1',aliasName2: 'getterName2'})
  ...mapGetters({'在当前组件中使用的名字': 'getter的名字'})
  ...mapGetters({aliasName1: state => state.moduleName.stateName1,})
}
methods:{
 使用方法fun() {
       this.getterName1();
    }
}
(4)actions使用 辅助函数:mapActions
  • actions是vuex的一个配置项
  • 作用:发异步请求获取数据,调用mutations来保存数据,将整个ajax操作封装到Vuex的内部
  • 要点:
    • action 内部可以发异步请求操作
    • action是间接修改state的:是通过调用 mutation来修改state

       定义格式:

new Vuex.store({
  // 省略其他...
  actions: {
    // context对象会自动传入,它与store实例具有相同的方法和属性
    action的名字: function(context, 载荷) {
      // 1. 发异步请求, 请求数据
      
      // 2. commit调用mutation来修改数据
      
      // context.commit('mutation名', 载荷)
    }
  }
})

action一般用来发异步请求,数据回来之后,在去调用mutations来保存数据

        

// 方法一(直接使用)
this.$store.dispatch('actions的名字', 参数)

// 方法二(引入,之后可视为methods方法使用)
// 基于 Vuex 提供的 mapActions 辅助函数,可以方便的把 Store 中指定的 Action,映射为当前组件的 methods
// 使用之前需要引入mapActions,方法: import { mapActions } from 'vuex';
methods: { 
  ...mapActions(['actions的名字']), 
  ...mapActions({'在当前组件中使用的名字': 'actions的名字'}),

 使用方法fun() {
       this.actions的名字();
    }
}
应用场景二(用modules来拆分复杂业务):
export default new Vuex.Store({
    // state: 用来保存所有的公共数据
    state: {},
    getters: {},
    mutations: {},
    actions: {},
    modules: {
    模块名1: {
            // namespaced为true,则在使用mutations时,就必须要加上模块名
            namespaced: true, 
            state: {},
            getters: {},
            mutations: {},
            actions: {},
            modules: {}
    },
    模块名2: {
        // namespaced不写,默认为false,则在使用mutations时,不需要加模块名
          state: {},
          getters: {},
          mutations: {},
          actions: {},
          modules: {}
    }  
  }
})

在组件中使用:

// state使用
this.$store.state.模块名.变量名
...mapState('模块名',['变量名1','变量名2'])

// mutations使用
this.$store.commit('模块名/mutation名', 实参);
...mapMutaions('模块名',['方法名'])

// getters使用
this.$store.dispatch.模块名.变量名
...mapGetters('模块名',['变量名1','变量名2'])

// actions使用
this.$store.mapActions('模块名/actions名', 实参);
...mapActions('模块名',['方法名'])

vuex模块化:

当我们开发的项目比较大时,store中的数据就可能比较多,这时我们store中的数据就可能变得臃肿,为了解决这一问题,我们就需要将store模块化(module),即每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块

注意:被模块化的store须要开启命名空间。

模块A:a.js

export default {
    namespaced: true,//开启命名空间
    state: {
    	sum:0,
    	number:0
    },
    mutations: {
    	ADD_NUM(state,value){
  			state.sum+=value
  		}
    },
    actions: {
    },
}

模块B:b.js

export default {
    namespaced: true,//开启命名空间
    state: {
    	name:'张三',
    	personList:[]
    },
    mutations: {
    	ADD_PERSON(state,value){
    		state.personList.unShift(value)//把数据添加到数组的首位
    	}
    },
    actions: {
    	addZhang(context,value){
    		//value参数是一个对象
    		if(value.name.indexOf('张')===0){
    			context.commit('ADD_PERSON')
    		}else{
    			alert('这个人不姓张!')
    		}
    	},
    	addServer(context,value){
    		axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(res=>{
    			context.commit('ADD_PERSON',{name:res.data})
    		},error=>{
    			alert(error.message)
    		})
    	}
    },
    getters: {
    	firstName(state,getters){
 			return state.personList[0].name+',你好!'
 		}
    }
}

store/index.js:在modules中引入

import Vue from 'vue'
import Vuex from 'vuex'

//引入
import moduleA from './a.js'
import moduleB from './b.js'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {//模块化
    moduleA,
    moduleB
  }
})

这时我们在项目中如何取用呢?

  1. 访问state数据:
    第一种方式:this.$store.state.moduleA.sum
    第二种方式:
     import {mapState} from 'vuex'
     computed:{
     	...mapState('moduleA',['sum','number'])
     }
    

  2. 修改state数据:
    第一种方式:this.$store.commit('moduleA/ADD_NUM',10)
    第二种方式:
     import {mapMutations} from 'vuex'
     methods:{
    	...mapMutaions('moduleA',['ADD_NUM']),
     	}
    

vuex3和vuex4异同点:

        1) 创建 Store 的方式

  • Vuex 3:使用 new Vuex.Store() 创建 store 实例
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vue)

const store = Vuex.Store({
    ...配置项
})
export default store
  • Vuex 4:使用 createStore 创建 store 实例
import {createStore} from 'vuex'
const store = createStore({
    ...配置项
})

export default store

Vuex 4 中使用 createStore 函数来创建 store 实例,而不是直接在 Vue 实例上挂载

        2) 在组件中使用 Store

  • Vuex 3:使用 this.$store 访问 store 实例,通过 this.$store.state 访问状态,通过 this.$store.commit() 进行提交 mutation,通过 this.$store.dispatch() 进行分发 action。
export default {
    name:"#app",
    computed:{
        count(){
        return this.$store.state.count 
        }
    },
    methods:{
        increment(){
            this.$store.commit('increment')
        },
        incrementAysnc(){
            this.$store.dispatch('incrementAysnc')
        }
    }
}
  • Vuex 4:使用 useStore 函数来获取 store 实例,通过 store.state 访问状态,通过 store.commit() 进行提交 mutation,通过 store.dispatch() 进行分发 action
import {useStore} from 'vuex'
export default {
    setup(){
        const store = useStore()
        const count = computed(()=>store.state.count)

        const increment = () =>{
            store.commit('increment ')
        }

        const incrementAsync = () =>{
            store.dispatch('incrementAsync')
        }

        return {
            count,
            increment,
            incrementAsync 
        }
    }
}

虽然 Vuex4 推荐使用更符合 Composition API 风格的 useStore() 来获取 store 实例。但是并没有移除 this.$store,但是在 <template> 和 Vue2 选项式写法中还是支持使用 $store 的。

3)辅助函数

  • Vuex 3:使用 mapStatemapGettersmapMutations 和 mapActions 辅助函数来进行映射,简化在组件中对 store 的访问。
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex';
export default {
  name: 'App',
   computed:{
    ...mapState['count'],
    ...mapGetters['doublecount'],
  },
    methods:{
    ...mapMutations['increment'],
    ...mapActions['incrementAsync'],
    }
}
  • Vuex 4:使用 Composition API 中的 computed 函数和普通的 JavaScript 函数来实现类似的功能。
import {computed,useStore} from 'vuex'

    export default {

        steup(){
            
            const store = useStore()

            const count = computed(()=>store.state.count)

            const doublecount = computed(()=>store.getters.doublecount)

            const increment = () => {
                store.commit('store ')
            }
            
            const incrementAsync = () => {
                store.dispatch('incrementAsync')
            }

            return {
                count,
                doublecount,
                increment,
                incrementAsync 
            }
        }

    }

 Vuex4 支持选项式写法的辅助函数,在使用时和 Vuex3 一模一样的。但是需要注意辅助函数不能在组合式写法 setup 中使用。

4)Vuex4 支持多例模式

Vuex 3 是单例模式的,即整个应用只能有一个全局的 Vuex Store 实例。而在 Vuex 4 中,你可以通过 useStore 函数在不同组件中创建多个独立的 Vuex Store 实例,从而支持多例模式。

以下是一个示例展示了如何在 Vuex 4 中使用 useStore 辅助函数创建多个独立的 Vuex Store 实例:

<template>
  <div>
    <p></p>
    <p></p>
    <button @click="incrementcount1">Increment count 1</button>
    <button @click="incrementcount2">Increment count 2</button>
  </div>
</template>
<script>
    import {useStore} from 'vuex'
    export default {
        setup(){
            //使用useStore 创建 Vuex store实例
            const store1 = useStore('store1')
            const store2 = useStore('store2')

            //通过store1.state.count1 获取第一个store实例状态
            count count1 = store1.state.count1
            //通过store2.state.count2 获取第二个store实例状态
            count count2 = store2.state.count2

            //通过store1.commit 提交mutation第一个store状态
            const increment1 = () => {store1.commit('increment1 ')}

            //通过store2.commit 提交mutation第二个store状态
            const increment2 = () => {store2.commit('increment2 ')}


            return {
                count1,
                count2,
                increment1,
                increment2 
            }
        }
        
    }

</script>

上述示例展示了如何在 Vue 组件中使用 useStore 辅助函数创建多个独立的 Vuex Store 实例,并通过这些实例分别访问和修改各自的状态和 mutations。这是 Vuex 4 相对于 Vuex 3 的一个重要的改进,使得 Vuex 在支持多例模式的场景下更加灵活和可扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记忆深处里的海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值