Day43:VUEX

news/2024/2/29 2:20:28

1. 基本介绍

这里介绍的VueX是匹配Vue3的V4版本,它绝大多数功能使用都和之前基本保持一致。

1.1 官方定义

先一起看一下官网对于VueX的定义:

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

1.2 状态管理

官网定义中提到了一个现代前端中非常重要的概念状态管理 ,怎么来理解这个概念呢?

  • 在Vue中我们会定义一些驱动应用的响应式数据,我们把这些数据叫做状态(state)
  • 定义的初始化数据会按照Vue的模板语法以声明式的方式映射到视图(view)
  • 用户在视图上的操作(actions)会引起状态变化从而导致视图刷新

在复杂的应用中,我们会经常遇到以下的问题:

  • 多个视图依赖于同一状态。
  • 来自不同视图的操作需要变更同一状态。

其实这些问题我们也能解决,但需要状态在组件之间反复传递,十分容易出现错误,开发和维护成本都非常高,我们为什么不把组件的共享状态抽取出来,放到一个单独的空间管理呢,这个空间可以被任意组件访问到,这其实就是VueX的设计思想

1.3 VueX的必要性

并不是所有的项目中都需要使用VueX,如果你不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果你的应用够简单,你最好不要使用 Vuex。但是如果你需要构建一个中大型应用,在组件外部管理状态往往是一个更好的选择,那么这个时候VueX便会是自然而然的选择了。

1.4 项目安装

通过npm命令在项目中安装VueX

npm i vuex --save

在package.json中确认正确的版本号

1.5 基本使用

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

安装好VueX后,在src目录下新建一个store文件夹,再建立一个index.js文件,我们在这个js文件中来创建我们的store容器。

import { createStore } from 'vuex'// 创建一个新的 store 实例
const store = createStore({state() {return {count: 0,}},mutations: {increment(state) {state.count++},},
})
export default store

然后在入口文件main.js中引入store并使用它。这里的store和之前的router都可以理解为Vue的插件,都可以调用use方法。

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'createApp(App).use(store).mount('#app')

好了,现在我们的项目中就可以通过VueX来进行全局的状态管理了,接下来我们要深入学习其中的核心概念!

vue2 index.js

npm i vuex@3

import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({render: h => h(App),store:store, // 可以简写
}).$mount('#app')
import Vue  from 'vue'
import VueX  from 'vuex'
Vue.use(VueX)
const store = new VueX.Store({state(){return {count: 1,msg:'公共状态'}},mutations: {// 定义修改数据的方法addCount(state, payload) {state.count += payload.a}}
})
export default store
<template><div><button @click="addCount">{{ count }}</button><p>{{ msg }}</p></div>
</template>
<script>
// 映射函数,可以将公共状态映射到当前组件
import { mapMutations, mapState } from 'vuex'
export default {mounted() {console.log(this)},computed: {...mapState({count: (state) => state.count,msg: (state) => state.msg, // 这里可以改名字,但是不推荐改})},methods: {add() {// 违背了数据单向流动的原则// this.$store.state.count++// this.$store.commit通过这个方法修改了公共状态值this.$store.commit('addCount', { a: 10, b: "haha" })} }
}
</script>

2.state

2.1 保存状态

store中保存的状态同样是具有响应性的,我们把应用中的公共状态统一保存到state属性中。

js

const store = createStore({state() {return {count: 0, //这里的count后面就可以被应用中所有的组件共享}},
})

2.2 读取状态

在项目中任意创建了一个Hello组件,现在希望在这个组件中取出刚才的count。由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:

在组件中获取到store必须调用useStore这个方法(和vue-router类似,在Vue3的最新生态中推荐使用函数的方式来获取示例,而非this)

vue

//Hello组件中<template><h2>{{ count }}</h2></template><script setup>import { useStore } from 'vuex'import { computed } from '@vue/reactivity'let count = computed(() => {return useStore().state.count})</script>

2.3 映射函数

映射函数,可以将公共的状态映射到当前的组件,再在当前组件中调用就会简便很多。如果你使用的是Vue2版本的配置选项写法,使用映射函数往往非常高效。当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性:

import { mapState } from 'vuex'export default {// ...computed: {...mapState({// 箭头函数可使代码更简练count: state => state.count,// 传字符串参数 'count' 等同于 `state => state.count`countAlias: 'count',// 为了能够使用 `this` 获取局部状态,必须使用常规函数countPlusLocalState (state) {return state.count + this.localCount}})}}

2.4 组件独立的状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

VUE2实现

<template><div><button @click="addCount">{{ count }}</button></div>
</template>
<script>
import { mapMutations, mapState } from 'vuex'
export default {methods: {...mapMutations(["addCount"])}
}
</script>

vuex中mutations里都是同步修改数据的方法, 如果希望异步修改数据,需要把对应的方法,定义在actions的对象中

但是actions不能直接修改state,最终还是要通过触发mutations来实现。

引用的方法同样可以映射

<template><div><button @click="addCount">{{ count }}</button><button @click="addAsyncCount(3)">{{ count }}</button> <!-- 异步按钮,三秒后响应,增加3 --><p>{{ msg }}</p></div>
</template>
<script>
import { mapMutations, mapState, mapActions } from 'vuex'
export default {mounted() {console.log(this)},computed: {...mapState({count: (state) => state.count,msg: (state) => state.msg, })},methods: {...mapMutations(["addCount"]),...mapActions(["addAsyncCount"]),}
}
</script>

3.getter

3.1设置getters

有时候我们需要从 store 中的 state 中派生出一些状态,通过计算属性即可实现,但如果有多个组件需要用到这个计算属性,我们要么复制这个计算属性,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。

const store = createStore({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]},getters: {doneTodos (state) {return state.todos.filter(todo => todo.done)}}
})

3.2 读取getters

和state类似,同样可以在组件中通过store实例来读取到对应的getter。

//Hello中
let doneTodos = computed(()=>{return useStore().getters.doneTodos
})

3.3 映射函数

mapGetters 辅助函数同样可以将 store 中的 getter 映射到局部计算属性中,如果你使用配置选项仍然推荐你这么做,在setup语法糖中这样使用的意义就没那么大了。

import { mapGetters } from 'vuex'
export default {computed: {// 使用对象展开运算符将 getter 混入 computed 对象中...mapGetters(['doneTodosCount','anotherGetter',])}
}

VUE2实现

4.mutation

4.1 提交commit

VueX规定必须通过mutation来能修改state中的状态。mutations中保存了修改了state的各种方法,在组件中可以通过store.commit来触发对应的方法,同时传入参数(这个参数叫做载荷)

//store
mutations: {increment(state, n) {state.count += n},
},
//Hello<template><h2>{{ count }}</h2><button @click="add">点我count增加</button><div v-for="todo in doneTodos" :key="todo.id"><h3>{{todo.text}}</h3></div></template><script setup>import { useStore } from 'vuex'import { computed } from '@vue/reactivity'const store = useStore()let count = computed(() => {return store.state.count})let doneTodos = computed(()=>{return store.getters.doneTodos})const add = ()=>{store.commit('increment',5)}</script>

4.2 mutation必须是同步函数

VueX是规定mutations中修改状态必须是同步的,我们可以尝试写一段异步代码:

mutations: {increment(state, n) {setTimeout(() => {state.count += n}, 2000)},
},

大家肯定会疑问,代码没问题呀页面也能够正确响应状态的变化,但是VueX设计思想中需要能够捕捉到mutation前后状态的快照,异步代码无法让VueX能够了解其执行时机,这样描述可能大家此时仍然不好理解,所以现在你需要记住这个结论!

5.action

action和mutation有些类似,但不同在于:

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

Action 函数第一个参数接受一个与 store 实例具有相同方法和属性的 context对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

Action第二个参数也接受一个载荷。

actions:{increment(ctx,payload){setTimeout(()=>{ctx.commit('increment',payload)},2000)}
}

在组件中视图中,可以通过事件来触发action。所以之前的异步操作可以通过分发action来实现。

const add = () => {store.dispatch('increment',2)
}

6.module

6.1 嵌套模块

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

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

const moduleA = {state: () => ({ ... }),mutations: { ... },actions: { ... },getters: { ... }
}const moduleB = {state: () => ({ ... }),mutations: { ... },actions: { ... }
}const store = createStore({modules: {a: moduleA,b: moduleB}
})store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

6.2 命名空间

默认情况下,模块内部的 action 和 mutation 仍然是注册在全局命名空间的——这样使得多个模块能够对同一个 action 或 mutation 作出响应。Getter 同样也默认注册在全局命名空间。必须注意,不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。例如:

const store = createStore({modules: {account: {namespaced: true,// 模块内容(module assets)state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响getters: {isAdmin () { ... } // -> getters['account/isAdmin']},actions: {login () { ... } // -> dispatch('account/login')},mutations: {login () { ... } // -> commit('account/login')},// 嵌套模块modules: {// 继承父模块的命名空间myPage: {state: () => ({ ... }),getters: {profile () { ... } // -> getters['account/profile']}},}}}
})

命名空间在多人协作处理大型应用的时候还是非常具有实际意义的。

vue2实现

分别为login和profile新建store项目,可以分别为不同的组件书写不同的store

import Vue from 'vue'
import VueX from 'vuex'
import loginStore from "./loginStore";
import profileStore from "./profileStore";
Vue.use(VueX)
const store = new VueX.Store({modules:{login:loginStore,profile:profileStore,}
})
export default store

在app中可以通过模板来获取到子组件store中的值

// 代表Store中modules下,login这个store中a元素的值
<p>{{ $store.state.login.a }}</p>

在store中,存放需要在组件中流通的公共状态

不需要流通的状态和属性还是仍然放在原本的组件中


https://www.jiucaihua.cn/news/show-5071409.html

相关文章

负载均衡原理及应用

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Leetcode168. Excel表列名称

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题解&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 代码如下&#xff1a; class Solution {public String convertToTitle(int columnNumber) {StringBuild…

Shell脚本编写:从零到精通

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Python实现四维图像绘制系统

文章目录 动图绘制系统的实现播放控制接口优化总结 Python绘图系统&#xff1a; &#x1f4c8;从0开始的3D绘图系统&#x1f4c9;一套3D坐标&#xff0c;多个函数&#x1f4ca;散点图、极坐标和子图自定义控件&#xff1a;绘图风格&#x1f4c9;风格控件&#x1f4ca;定制绘图…

2817. 限制条件下元素之间的最小绝对差;2305. 公平分发饼干;878. 第 N 个神奇数字

2817. 限制条件下元素之间的最小绝对差 核心思想&#xff1a;枚举二分。我们去枚举nums[j]&#xff0c;然后用一个数据结构去装nums[i]&#xff0c;利用二分去找在这个数据结构中离nums[j]最近的值的下标&#xff0c;然后统计最小值。这个数据结构可以使用SortedList&#xff…

【Vue.js】生命周期之基本使用

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》 《springMvc使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 目录 ​编辑 1.Vue是什么 2.Vue的特点及优势 3. 使用Vue的详细步骤 3.1.导入 3.2…

网络安全(黑客)自学​

前言 作为一个合格的网络安全工程师&#xff0c;应该做到攻守兼备&#xff0c;毕竟知己知彼&#xff0c;才能百战百胜。 计算机各领域的知识水平决定你渗透水平的上限。 【1】比如&#xff1a;你编程水平高&#xff0c;那你在代码审计的时候就会比别人强&#xff0c;写出的漏洞…

无涯教程-JavaScript - CEILING.MATH函数

描述 CEILING.MATH函数将数字四舍五入到最接近的整数或最接近的有效倍数。 Excel CEILING.MATH函数是Excel中的十五个舍入函数之一。 语法 CEILING.MATH (number, [significance], [mode])争论 Argument描述Required/OptionalNumberNumber must be less than 9.99E307 and …

王道数据结构C语言循环链表基本操作实现

文章目录 一、循环单链表1.1初始化及判空操作1.2判断是否是尾结点 二、循环双链表2.1初始化2.2判空2.3判断尾结点2.4循环双链表的删除 一、循环单链表 1.1初始化及判空操作 其实循环链表就是在单链表&#xff08;双链表&#xff09;上做一点小小的优化 它是把尾结点的next指…

期权翻倍行情一个月会出现几次?

期权翻倍一个月出现几次&#xff1f;期权翻倍行情一周有2次左右&#xff0c;也是必做的行情&#xff0c;出现的时候敢不敢满仓的去交易呢&#xff0c;从数据来说&#xff0c;每周2次&#xff0c;一个月8次机会最少也能吃到期权翻倍行情。本文来自&#xff1a;期权酱 期权行情在…