vue3 基础-Vuex 全局数据状态管理
针对页面间, 组件间的数据共享问题, Vue 提供一个数据管理框架 Vuex, 早期主要是用于 Vue2 , 而现在用 Vue3 也是可以正常使用, 但在 Composition API 下则会感到这个数据管理流程不论同步还是异步, 总感觉有点繁琐和反人类, 确实是不如它的竞品 Pinia, 但还是相对成熟, 也许后面再慢慢过渡吧.
- Vuex 能创建一个全局唯一的 store(仓库), 用来存放全局的数据
- Vuex 是一个响应式的数据状态管理框架 , 状态修改需要显示地提交 commit 一个任务流程
演示环境搭建
Vue3 + Vite
首先得安装 NodeJs 环境, 然后我这里的 node 版本是 v16, Npm 版本是 8.5, 创建一个项目文件夹 vue3+vite
# 0. 安装 NodeJs 的环境
C:\Users\Desktop\vue3+vite>npm -v
8.5.0
C:\Users\Desktop\vue3+vite>node -v
v16.14.2
通过 vite 官网介绍, 创建 vue3 的项目, 项目名称为 youge, 然后进项项目初始化并启动, 如下:
# npm 7+, extra double-dash is needed:
-- 1. 创建项目
npm create vite@latest youge -- --template vue
Done. Now run:
cd youge
npm install
npm run dev
-- 2. 进入项目文件夹
C:\Users\Desktop\vue3+vite>cd youge
-- 3. 初始化项目
C:\Users\Desktop\vue3+vite\youge>npm install
added 25 packages in 5s
-- 4. 重启项目并在浏览器运行
C:\Users\Desktop\vue3+vite\youge>npm run dev
VITE v4.5.0 ready in 1379 ms
➜ Local: http://127.0.0.1:5173/
➜ Network: use --host to expose
➜ press h to show help
这里的 vue 代码高亮和拼写提示 的 vscode 插件是 Volar 和 vue3-snippets-for-vscode
安装上 vuex
这里的版本选择 4x , 安装如下:
npm install vuex@next --save
在项目 src 下创建一个名为 store 的文件夹, 里面创建一个名为 index.js 的文件
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
name: "youge",
count: 666
}
},
// 同步操作
mutations: {
increment(state) {
state.count++
}
},
// 异步操作
actions: {
}
})
export default store
然后在 main.js 中对 store 进行全局引入
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
Vuex 数据管理
数据获取
在 App 这个演示组件 (页面) 模板中,
- 行内写法: 在 template 中, 可直接通过 $store.state.xxx 获取数据, 适合简单场景
- 数据写法: 在 script 中通过 vuex 提供的 useStore 进行实例化, 配合 computed 进行获取
<!-- App.vue -->
<template>
<div>
<p>直接获取 state 中的数据: </p>
<p>行内: {{ $store.state.name }}</p>
<p>脚本: {{ count }}</p>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.state.count)
</script>
显示如下:
直接获取 state 中的数据:
行内: youge
脚本: 666
同步任务修改: Mutations
可直接在 DOM 中进行行内的写法, 直接进行 $store.commit('同步触发', 参数值)
<!-- App.vue -->
<template>
<div>
<p>直接获取 state 中的数据: </p>
<p>行内: {{ $store.state.name }}</p>
</div>
<button @click="$store.commit('changeName', 'jack')">修改全局的姓名</button>
<p>其他组件也引用 name 值: {{ $store.state.name }}</p>
</template>
当然通过在 script 中写也是一样的. useStore 进行实例进行 commit 也一样.
<!-- App.vue -->
<template>
<div>
<p>直接获取 state 中的数据: </p>
<p>行内: {{ $store.state.name }}</p>
</div>
<button @click="handleClick('jack')">修改全局的姓名</button>
<p>其他组件也引用 name 值: {{ $store.state.name }}</p>
</template>
<script setup>
import { ref } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const handleClick = (name) => store.commit('changeName', name)
</script>
即该按钮向 store 显式 commit 了一个任务叫 'changeName' 并携带了一个参数 'jack' 过来. 因此在 store 中同步进行处理如下:
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
name: "youge",
count: 666
}
},
// 同步操作
mutations: {
// 处理由上例按钮提交过来的同步任务: changeName
changeName(state, name) {
state.name = name
}
},
})
export default store
则当页面进行按钮点击的时候, 就会修改全局数据了, 且是响应式的, 即可全局的 name: youge, 改成了 name: jack
直接获取 state 中的数据:
行内: jack
点击: 修改全局的姓名
其他组件也引用 name 值: jack
总结一下关于同步操作的步骤就是直接提交任务到 mutations 即可:
- 提交 dom 中 @click="$store.commit('changeName', 'jack')" 同步任务
- 处理 dom 中 提交过来的任务, 即在 store 的 mutations 中定义 changeName(state, name) 方法
- 应用 dom 中 可直接通过 $store.state.xxx 直接应用全局数据即可
异步任务修改: Actions
当处理异步任务时, 如用户登录, 获取用户信息, 权限, 用户退出等, vuex 就不让直接 commit 给 mutations 了. 它的操作是先通过 dom 先给 actions 派发 (dispatch ) 一个任务或事件A, 然后通过在 actions 中 commit 一个事件B 给到 mutations, 然后 在 mutations 中完成对事件B 的处理.
就是, 我感觉有点啰嗦, 没有 Piana 那样不区分同步和异步任务, 直接干就行. 但还是演示一下吧.
先在 dom 中进行异步任务派发 一个异步事件, 如叫 'asyncChangeName'
<!-- App.vue -->
<template>
<div>
<p>直接获取 state 中的数据: </p>
<p>行内: {{ $store.state.name }}</p>
</div>
<button @click="handleClick('jack')">异步修改全局的姓名</button>
<p>其他组件也引用 name 值: {{ $store.state.name }}</p>
</template>
<script setup>
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.state.count)
// 异步修改全局 name: 先 dispatch -> actions -> commit -> xxx
const handleClick = (name) => store.dispatch('asyncChangeName', name)
</script>
或者也写成内行的方式在 dom 上
<!-- App.vue -->
<template>
<div>
<p>直接获取 state 中的数据: </p>
<p>行内: {{ $store.state.name }}</p>
</div>
<button @click="$store.dispatch('asyncChangeName', 'jack')">异步修改</button>
<p>其他组件也引用 name 值: {{ $store.state.name }}</p>
</template>
然后 store 中的 actions 进行接收 'asyncChangeName', 并再提交给 mutaions , 让其进行处理.
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
name: "youge",
}
},
// 同步操作
mutations: {
changeName(state, name) {
state.name = name
}
},
// 异步操作
actions: {
// 这里的 name 就是节点 dispatch 过来的实参 'jack'
asyncChangeName({ commit }, name) {
// 模拟异步, 等 2s 后再进行提交, 在实际中可以结合 Promise 进行状态管理, 如用户登录处理
setTimeout(() => {
commit('changeName', name)
}, 2000);
}
}
})
export default store
然后点击按钮后, 等待 2 秒, 数据发生了改变.
就这个异步处理流程来说, 直观上感觉 actions 像一个二道贩子, 中间转手一道, 最后还是 commit 给了 mutations 进行处理. 这和我司的渠道很像, 就 mutations 是直供, 省公司直接到门店; 这个 actions 是传统二代, 即省代 - 二代 - 门店.
但从结果来看, 各种模式都有各自的优缺点, 只能有持续经营, 都是可行的.
总结一下关于异步操作的步骤, 即要通过 actions 转一手到 mutaions :
- 提交 dom 中 @click="$store.dispatch('asyncChangeName', 'jack')" 异步任务
- 处理 dom 中 接收异步任务 即在 store 的 actions 中定义 asyncChangeName({ commit }, name) 方法
- 在 asyncChangeName 中通过 commit 给 mutations 里面的方法 (changeName) 进行实际处理
- 应用 dom 中 可直接通过 $store.state.xxx 直接应用全局数据即可
也是兴趣之余, 写了两个项目才真正搞清楚这个 vuex 的基本流程, 学习这个事情就还是,
纸上来得终觉浅, 绝知此事要躬行.