了解vuex4.x基础用法与对比vue3.x的差异

简介

Vuex是Vue.js应用程序的状态管理模式+库。它充当应用程序中所有组件的集中存储,其规则确保只能以可预测的方式更改状态。

简而言之就是提供一种状态管理的库,并且状态的改变是可预测的。

状态: 我个人理解为在你的组件中的任何一个变量都可以是状态。只要你想,都可以写进状态,但我相信你不会这样做的。

安装

  1. 已有项目可使用vue add vuex 添加vuex。
  2. 创建项目时可以选择 Manually select features 自定义配置。
  3. npm install vuex@next --save,需要手创建store文件,建议使用前两种。

安装好后项目src文件下将创建一个store文件夹,其内部存在一个index文件

// index.js

import { createStore } from 'vuex'

export default createStore({
	state: {},
	getters: {},
	mutations: {},
	actions: {},
	modules: {}
})

vuex4.x为了保持和vue3创建方式一致,所以建议使用createStore创建

核心概念

state

存储数据

往state添加一个变量 count,此时 count 将在项目下的任何组件内都可以访问。state内的数据称之为状态。在state中存储数据与data内存储数据规则相同。

state(){
    count: 0
}

组件内访问count

  • 模板内 $store.state.count
  • 对象内 this.$store.state.count

由于Vuex商店是反应式的,因此从中检索状态的最简单方法是简单地从计算属性中返回一些商店状态:

computed(){
    count(){
        return this.$store.state.count;
    }
}

此时就可以通过计算属性count访问store中的数据count。每当store中的count修改时,将导致重新计算计算属性,并触发关联的DOM更新。

如果需要引入的状态过多,当给每个状态绑定计算属性时,显然代码会变得重复和冗长。为了解决这个问题,导入 mapState 方法。

mapState可以帮助我们生成计算的state函数。

mapState

需要从 vuex 中导入 mapState 模块

import { mapState } from 'vuex'

使用mapState有两种方法

// 传入对象形式
computed: mapState({
  // 第一种
  count: state => state.count,
  // 第二种:传递字符串 'count' 等同于 state => state.count
  countAlias: 'count',
  // 第三种:结合当前组件状态  data中定义localcount
  countPlusLocalState(state){
    return state.count + this.localcount;
  }
})

mapState当映射的计算属性的名称与状态子树名称相同时,我们还可以传递字符串数组。

computed: mapState([ 'count' ])

访问时

this.count

store的computed 与 组件的computed结合

当前组件的计算属性(computed) 与store的计算属性(computed) 一起使用,就需要利用 对象扩展运算符

mapState返回一个对象

computed: {
  localCountDecorate(){
    return `本地组件状态${this.localcount}`
  },
  // 使用传递数组的方式,也可以使用传递对象的形式
  ...mapState([ 'count' ])
}

组件计算属性写在mapState内同样有效,但接受的参数不同,所以需要分开

组件仍可以具有本地状态

使用Vuex并不意味着您应该将所有状态都放入Vuex。尽管将更多状态添加到Vuex中可以使您的状态突变更明确和可调试,但有时它还可以使代码更冗长和间接。如果一个状态严格地属于单个组件,那么将其保留为本地状态就可以了。

getters

getters可以称之为装饰器,他可以将state中的数据做一层处理(装饰/筛选),返回给组件。

首先在state中创建一个状态(商品列表)

articleList: [
      {id: '001', articleName: '* cotton', description: '*盛产优质长绒棉', stock: 1000000, price: 100},
      {id: '002', articleName: 'shoe', description: 'LiNing 韦德之道4', stock: 10, price: 100},
      {id: '003', articleName: 'Graphics card', description: '3080系列显卡', stock: 0, price: 100},
      {id: '004', articleName: 'vaccine', description: '新型冠状病毒疫苗', stock: 100000000, price: 100},
      {id: '005', articleName: 'xiaomi 11', description: 'xioami 第11代手机', stock: 100000, price: 100},
      {id: '006', articleName: 'Kirin Chip', description: '华为 麒麟芯片', stock: 0, price: 100}
    ]

在getters中对其进行筛选

getters: {
	Instock(state, getters){ // state作为第一个参数, getters 访问其他getters
		console.log(getters);
		return state.articleList.filter(item => item.stock != 0);
	}
}

getters接收两个参数

  • state: 访问当前store中的state
  • getters: 访问当前store中的其他getters

现在就对state中的状态articleList经行一次筛选,把stock 为0的都去掉。

组件中访问

访问方式同state

computed: {
	Instock(){ // 可不使用同名
      return this.$store.getters.Instock;
    }
}

同样可使用mapGetters映射到computed,减少代码量。

mapGetters

规则同mapState

computed: {
	...mapState(['Instock'])
}

访问时

this.Instock()

获取指定数据

getters可返回一个函数以此接收 一个参数的传入,用于查找数据。

// getters
searchArticle: (state) => (id) => { // 返回函数传入id,使用id查找数据
	return state.articleList.filter(item => item.id == id);
}

组件中

...mapGetters([ 'Instock', 'searchArticle' ])

此时可通过传递参数以使返回指定数据

this.searchArticle('001'); // 指定返回id为001的数据

getters只有对state中的状态进行装饰并返回的能力,并不能直接改变state。改变state需要使用mutations

mutations

state中的状态改变只能在mutations中。并且mutations中不能存在异步操作

mutations: {
	addCount(state, payload){
		// 接收组件内执行传递的参数累加
		state.count += payload.count;
	}
}

可接受两个参数

  • state:访问当前store的state
  • payload: 传递的参数。大多数情况下应为对象,以此传递更多的状态

组件中 methods 中声明。mutations通过commit提交改变。

// template
<button @click="addCount">增加</button>

// js
methods: {
	addCount(){
		this.$store.commit('addCount', {count: 1}); // {count: 1} 传递的参数
	}
}

简化:mapMutations

import { mapMutations } from 'vuex';
methods: {
	// 传递数组
	...mapMutations(['addCount']) // this.addCount() 执行
	// 传递对象(赋新值)
	...mapMutations({ localtotal: 'addCount' }) // this.localtotal() 执行
}

在有些情况下,会不可避免的在更改数据时进行请求或者其他异步操作,而mutations中不允许使用异步操作。此时将使用 actions

actions

动作类似于mutations(突变),不同之处在于:

  • 动作不会改变状态,而是执行改变。
  • 动作可以包含任意异步操作。

actions没有改变state的权利,但是可以commit mutations,所以说actions只是执行改变。

actions: {
	asynCount(context, products){
		setTimeout(() => {
			context.commit('addCount', products);
		}, 1000);
	}
}

接收两个参数

  • context:store上下文对象。可使用解构,包含commit、dispatch、getters、rootGetters、state、rootState
  • products:传递的参数

组件中调用。actions通过dispatch触发

// template
<button @click="asynAddCount">一秒后加一</button>

// js
methods: {
	asynAddCount(){
		this.$store.dispatch('asynCount', { count: 3 });
	}
}

简化:mapActions

import { mapActions } from 'vuex';
methods: {
	// 传递数组
	...mapActions(['asynCount']) // this.asynCount() 执行

	// 传递对象
	...mapActions({ localAsyncTotal: 'asynCount' }) // this.localAsyncTotal() 执行
}

mudeles

随着我们应用程序规模的扩大,商店可能会变得臃肿。为了解决这个问题,Vuex允许我们将商店划分为模块。每个模块可以包含其自己的状态,变异,动作,获取器,甚至是嵌套模块-一直到整个过程都是分形的

项目越来越大的情况下,分模组是特别有必要的。
文件解构如下

store
└─ index.js
└─ moduleA.js
└─ moduleAInside.js
└─ moduleB.js

文件内代码

// index.js
import { createStore } from 'vuex'
import moduleA from './moduleA'
import moduleB from './moduleB'

export default createStore({
  state: {
    count: 0,
  },
  getters: {
  },
  mutations: {
    addCount(state, payload){
      state.count += payload.count;
    }
  },
  actions: {
    asynCount(context, products){
      setTimeout(() => {
        context.commit('addCount', products);
      }, 1000);
    }
  },
  modules: {
    moduleA,
    moduleB
  }
})
// moduleA.js
import moduleAInside from './moduleAInside'
const moduleA = {
    state: () => ({
        moduleAstate: '未知'
    }),
    getters: { ... },
    mutations: { ... },
    actions: { ... },
    modules: {
    	moduleAInside 
	}
}

export default moduleA;
// moduleAinside.js
const moduleAInside = {
    state: () => ({
        moduleAInsideState: '未知'
    }),
    getters: { ... },
    mutations: { ... },
    actions: { ... }
}

export default moduleAInside;
// moduleB.js
const moduleB = {
    state: () => ({
        moduleBstate: '未知'
    }),
    getters: { ... },
    mutations: { ... },
    actions: { ... }
}

export default moduleB;

模组内的用法和外部并无二样,同样模组内可继续嵌套模组。

关于模块内访问模块其他模块或者根模块。

  • **getters: ** getters的第三、四个参数 rootState、rootGetters 可以访问根模块和其他模块
  • **actions: ** actions的 context 包含 rootState、rootGetters,可用于访问其他模块与根模块。

高级

vue3推出了组合式api,在组合式api中可以使用 useStore 函数创建对 vuex 的引用,这等同于 this.$store

import { useStore } from 'vuex'

export default {
    setup(){
        const store = useStore();
    }
}

访问state和getters

由于我访问的是comAPI模块内的状态,所以在访问state时需要先访问模块后才可访问具体的状态,getters则不用。

const comState = computed(() => store.state.comAPI.comAPIState);
const comGetters = computed(() => store.getters.comAPIGetters);

访问mutations和actions

访问mutations和actions不需要考虑模块作用域问题。

const changeState = () => { store.commit('changeState') }
const asynChangeState = () => { store.dispatch('asynChangeState') }

对比vuex4 & vuex3

创建方式

vuex4与vue3的创建方式保持一致

import { createStore } from 'vuex'

export const store = createStore({
  ...
})

安装到vue

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

const app = createApp(App)
app.use(store)
app.mount('#app')

新增组合式API useStore

import { useStore } from 'vuex'

export default {
  setup () {
    const store = useStore()
  }
}
上一篇:vuex的核心概念和运行机制


下一篇:Vuex学习二:Vuex的重点属性学习,state、mutations、getters、actions、module。