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 的基本流程, 学习这个事情就还是,

纸上来得终觉浅, 绝知此事要躬行.

posted @ 2023-11-13 01:52  致于数据科学家的小陈  阅读(546)  评论(0编辑  收藏  举报