在Vue中,
Vue.js
的状态管理是通过Vuex
这个官方库来实现的。除了Vuex
这个官方库可以实现外,还有一个叫Pinia的库,Pinia
它也是Vue
的一个Store
库,它允许我们可以跨组件/页面共享状态。下面我们分别来学习它们吧。
什么是状态管理?
对于Vuex和Pinia的学习,我们先不着急,它们都是帮Vue实现状态管理的库,那么我们需要先了解什么是状态管理呢?
我们从一个简单的记数应用开始,所谓的状态管理就是你对数据的管理,一个状态自管理应用包含这几个部分:
- 状态------驱动应用的数据源;
- 视图 ------以声明方式将状态映射到视图;
- 操作 ------响应在视图上的用户输入导致的状态变化。
下面一张图解释这个简单记数应用的数据状态自管理吧。
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态,也就是这个count数据在多个组件内被使用。
- 来自不同视图的行为需要变更同一状态。
当多个组件使用同一个数据源时,涉及到的父子组件间状态通信就会变得很复杂,很繁琐,更不利于状态的变更,不同的子组件对状态的同步和更新不能一致,状态维护起来很艰难。为了解决这个问题,Vuex和Pinia,由此诞生了。
什么是Vuex?
Vuex
是一个专为 Vue.js 应用程序开发的状态管理模式和库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说就是将整个应用的状态集中起来管理,然后都按一致的规则对状态进行修改。
Vuex的应用也是需要看场景的,如果不打算开发大型单页应用,建议还是不用Vuex的,使用起来可能让代码繁琐冗余,简单的store模式足矣。下面我们将介绍如何使用Vuex。
Vuex 的核心概念包括:
-
State(状态):
- State 包含了整个应用的集中式的数据存储。
- 每个应用将只会有一个唯一的 store 对象。
-
Getters(获取器):
- 类似于计算属性(computed properties),但它是 store 的状态派生出来的结果。
- Getters 接受 state 作为第一个参数,并可以接受其他 getters 作为第二个参数。
-
Mutations(变异):
- 唯一更改 Vuex 的 store 中的状态的方法就是提交 mutation。
- Mutations 必须同步执行,并且可以被更细粒度地记录、测试等。
-
Actions(动作):
- Actions 类似于 mutations,不同在于:
- 而不是直接变更状态,actions 承载着一些异步操作并可以触发 mutations 来变更状态。
- Actions 可以包含任意异步操作。
- Actions 类似于 mutations,不同在于:
-
Modules(模块):
- 当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决这个问题,Vuex 允许我们将 store 分割成模块。
如何使用Vuex?
1. 为项目安装Vuex:
go
```
npm install vuex --save
```
2.设置Vuex Store:
接下来,我们将创建一个 Vuex store。在 src
目录下创建一个名为 store
的文件夹,并在其中创建一个名为 index.js
的文件。
src/store/index.js
:
scss
```
import { createStore } from 'vuex'
const store = createStore({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
}
},
actions: {
increment({ commit }) {
commit('increment')
},
decrement({ commit }) {
commit('decrement')
}
},
getters: {
count: state => state.count
}
})
export default store;
```
3.在主应用中使用 Vuex
现在我们需要在主应用中使用这个 Vuex store。
src/main.js
:
javascript
import { createApp } from 'vue'
import App from './App.vue'
import store from './store/index'
const app = createApp(App)
// 向全局注入store对象
app.use(store)
app.mount('#app')
创建 Vue 组件
接下来,我们创建一个 Vue 组件来显示计数器的值,并提供按钮来增加或减少计数值。
src/App.vue
:
xml
<script setup>
import { ref,computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const count = computed(() => store.getters.count)
const increment = () => store.commit('increment') // 唤醒一个 mutation 处理函数
const decrement = () => store.commit('decrement')
// const increment = () => store.dispatch('increment') // 唤醒一个 action 处理函数
// const decrement = () => store.dispatch('decrement')
</script>
这里的
import { useStore } from 'vuex';
和我们在组件内引入路由对象一样,也是使用useRouter
来获取全局路由。
- 通过查看Vuex的官方文档了解到,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler) 。
要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:
arduino
store.commit('increment')
// commit提交到mutations,提交的字符串type对应的是mutations内的处理函数
上面已经聊到 唯一更改 Vuex 的 store 中的状态的方法就是提交 mutation了,且他还有一个特点就是 Mutations 必须同步执行。
而对于Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
Actions的工作流程:
你可能会问这不是多此一举吗?既然都是在触发mutation ,那为什么不直接调用commit
去触发mutations
呢?
这就要谈到它的特殊部分了,在Actions内可以存在任何异步操作,在mutations内的代码只能同步执行, mutations内只是有这么一个只能同步执行的准则,大家都应该遵守的规则。
个人观点:
至于为什么,个人认为应该是mutations作为唯一准许修改数据状态的类似于事件的一个东西,他对数据状态不能模棱两可,当我们执行异步操作的时候,需要获取数据状态,然后在其之前执行的同步代码修改了数据,而异步还未执行,若异步用到同步代码修改的那个数据时,已经不是原来的数据了,而是同步代码修改后的数据,这样就乱套了。
结尾
下面用Vuex官网的例子,看看在actions
内如何实现异步的供大家观赏:
csharp
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData()) // 执行mutations内部的gotData方法
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData()) // 执行mutations内部的gotOtherData方法
}
}
好了,大概的Vuex的简单操作就是如此了,想要更加深入了解Vuex的jy可以去官网学习Vuex进阶了。链接丢这里 --->Vuex进阶 (vuejs.org)