首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >10-Vuex设计Vue3项目的数据流

10-Vuex设计Vue3项目的数据流

原创
作者头像
JavaEdge
发布于 2024-07-29 13:55:39
发布于 2024-07-29 13:55:39
2630
举报

1 前端数据管理

首先,我们需要掌握前端的数据怎么管理,现代Web应用都是由三大件构成,分别是:组件、数据和路由。

有一些数据组件之间需要共享时如何实现?定义一个全局变量,任何组件需要数据的时候都去这个全局变量中获取。一些通用的数据,比如用户登录信息,以及一个跨层级的组件通信都可以通过这个全局变量很好地实现。在下面的代码中我们使用_store这个全局变量存储数据。

代码语言:js
AI代码解释
复制
window._store = {}

数据存储的结构图如下,任何组件内部都可以通过window._store获取数据并且修改。

但这样就会产生一个问题,window._store并不是响应式的,如果在Vue项目中直接使用,那么就无法自动更新页面。所以我们需要用ref和reactive去把数据包裹成响应式数据,并且提供统一的操作方法,这其实就是数据管理框架Vuex的雏形了。

2 Vuex是啥?

其意义,就是管理我们项目的数据。

组件化机制搭建整个项目,每个组件内部有自己的数据和模板。但总有些数据要共享,如当前登录的用户名、权限等数据,如都在组件内部传递,很混乱。

如把开发项目比作公司,项目中各种数据像办公用品。很多小公司初创不需管理太多,随便拿办公用品。但是公司大了,就要专门的办公用品申报流程,对数据做统一申请发放,才方便做资产管理。 Vuex就相当于项目大管家,集中式存储管理应用的所有组件的状态

3 上手使用

src/store目录就是留给Vuex,项目目录下执行命令安装:

代码语言:bash
AI代码解释
复制
$ npm install vuex@next

3.1 创建数据存储

src/store下新建index.js。

用createStore创建一个数据存储,称store。

store内部还需要一个mutation配置去修改数据,可理解为数据更新的申请单,mutation内部函数会把state作为参数,直接操作state.count即可完成数据修改。

代码语言:js
AI代码解释
复制
import { createStore } from 'vuex'

const store = createStore({
  state () {
    return {
      count: 666
    }
  },
  mutations: {
    add (state) {
      state.count++
    }
  }
})

3.2 注册数据源

在Vue的组件系统外,多了一个数据源,里面只有一个变量count,且有一个方法可累加count。然后,在Vue中注册该数据源,在项目入口文件src/main.js,使用app.use(store)进行注册,Vue和Vuex就连接上了:

代码语言:js
AI代码解释
复制
...
import store from "./store";

...
app.use(store);

.use 即可注册路由,使用 .mount 即可把 Vue 这个应用挂载到页面上:

代码语言:js
AI代码解释
复制
const app = createApp(App)
app.use(store)
    .use(router)
    .mount('#app')

3.3 新建Count.vue组件

在src/components下新建Count.vue组件。

初始化值和修改的函数有①、②两个变化:

必须用add,不能直接操作 store.state.count +=1,因为这数据属Vuex统一管理,要用store.commit(‘add’)去触发Vuex中的mutation去修改数据。

代码语言:vue
AI代码解释
复制
<template>
<!-- 点击触发add方法 -->
<div @click="add">
  	<!-- 渲染count变量 -->
    {{count}}
</div>
</template>

<script setup>
import { computed } from 'vue'
// useStore去获取数据源
import {useStore} from 'vuex'
let store = useStore()
// ① count不是使用ref直接定义,而是使用计算属性返回store.state.count,即刚才在src/store/index.js中定义的count
let count = computed(()=>store.state.count)

// ② 用来修改数据
function add(){
    store.commit('add')
}
</script>

相比ref方式,真简单,那啥时数据用Vuex管理,啥时又要放在组件内部用ref管理?对于一个数据:

  • 如只是组件内部使用,就ref管理
  • 如需跨组件,跨页面共享时,就要把数据从Vue组件内部抽离,放在Vuex管理

如项目中的登录用户名,页面右上角要显示,有些信息弹窗也要显示。这样的数据就要放在Vuex统一管理。每当需要抽离这样的数据,都要思考这个数据的初始化、更新逻辑。

如下图,项目初始化时没有登录状态,用户登录成功后,才获取用户名信息,去修改Vuex数据,再通过Vuex派发到所有组件:

4 手写Vuex

先创建一个变量store存储数据。下一步就是把store的数据包转成响应式数据,并提供给Vue组件用。

在Vue中有 provide/inject 这两个函数专门用来做数据共享,provide注册数据后,所有子组件都可通过inject获取数据。

完成刚才的数据转换,进入src/store文件夹,新建gvuex.js。如下代码用一个Store类管理数据,类内部使用_state存储数据,使用mutations存储数据修改的函数,注意state已用reactive包裹成响应式数据。

代码语言:js
AI代码解释
复制
import { inject, reactive } from 'vue'

const STORE_KEY = '__store__'
function useStore() {
  return inject(STORE_KEY)
}
function createStore(options) {
  return new Store(options)
}
class Store {
  constructor(options) {
    this._state = reactive({
      data: options.state()
    })
    this._mutations = options.mutations
  }
}
export { createStore, useStore }

上面代码还暴露createStore去创建Store的实例,且可在任意组件的setup函数内,使用useStore去获取store的实例。下一步我们回到src/store/index.js,把vuex改成 ./gvuex。

下面代码用createStore创建一个store实例,且实例内部使用state定义count变量和修改count值的add函数:

代码语言:js
AI代码解释
复制
// import { createStore } from 'vuex'
import { createStore } from './gvuex'
const store = ...
export default store

最终用store的方式,在项目入口文件src/main.js中使用app.use(store)注册。为了让useStore能正常工作,下面的代码中,我们需要给store新增一个install方法,这个方法会在app.use函数内部执行。我们通过app.provide函数注册store给全局的组件使用:

代码语言:js
AI代码解释
复制
class Store {
  // main.js入口处app.use(store)的时候,会执行这个函数
  install(app) {
    app.provide(STORE_KEY, this)
  }
}

下面的代码中,Store类内部变量_state存储响应式数据,读取state的时候直接获取响应式数据_state.data,并且提供了commit函数去执行用户配置好的mutations。

代码语言:js
AI代码解释
复制
import { inject, reactive } from 'vue'
const STORE_KEY = '__store__'
function useStore() {
  return inject(STORE_KEY)
}
function createStore(options) {
  return new Store(options)
}
class Store {
  constructor(options) {
    this.$options = options
    this._state = reactive({
      data: options.state
    })
    this._mutations = options.mutations
  }
  get state() {
    return this._state.data
  }
  commit = (type, payload) => {
    const entry = this._mutations[type]
    entry && entry(this.state, payload)
  }
  install(app) {
    app.provide(STORE_KEY, this)
  }
}
export { createStore, useStore }

这样在组件内部,我们就可以使用这个迷你的Vuex去实现一个累加器了。下面的代码中,我们使用useStore获取store的实例,并且使用计算属性返回count,在修改count的时候使用store.commit(‘add’)来修改count的值。

代码语言:js
AI代码解释
复制
import {useStore} from '../store/gvuex'
let store =useStore()
let count = computed(()=>store.state.count)
function add(){
    store.commit('add')
}

这样借助vue的插件机制和reactive响应式功能,我们只用30行代码,就实现了一个最迷你的数据管理工具,也就是一个迷你的Vuex实现。

结合例子,正式介绍一下Vuex看一看Vuex具体咋用。

5 Vuex实战

Vuex就是一个公用版ref,提供响应式数据给整个项目使用。现在的功能还比较简单,项目大部分情况都是像之前的清单应用一样,除了简单的数据修改,还会有一些异步任务的触发,这些场景Vuex都有专门的处理方式。

在Vuex中,你可以使用getters配置,来实现computed的功能,比如我们想显示累加器数字乘以2之后的值,那么我们就需要引入getters配置。

下面的代码中,我们实现了计算累加器数字乘以2以后的值。我们在Vuex中新增了getters配置,其实getters配置和Vue中的computed是一样的写法和功能。我们配置了doubule函数,用于显示count乘以2的计算结果。

代码语言:js
AI代码解释
复制
import { createStore } from 'vuex'
const store = createStore({
  state () {
    return {
      count: 666
    }
  },
  getters:{
    double(state){
          return state.count*2
      }
  },
  mutations: {
    add (state) {
      state.count++
    }
  }
})

export default store

然后,我们可以很方便地在组件中使用getters,把double处理和计算的逻辑交给Vuex。

代码语言:js
AI代码解释
复制
let double = computed(()=>store.getters.double)

实际项目开发中,有很多数据我们都是从网络请求中获取到的。在Vuex中,mutation的设计就是用来实现同步地修改数据。如果数据是异步修改的,我们需要一个新的配置action。现在我们模拟一个异步的场景,就是点击按钮之后的1秒,再去做数据的修改。

面对这种异步的修改需求,在Vuex中你需要新增action的配置,在action中你可以做任意的异步处理。这里我们使用setTimeout来模拟延时,然后在action内部调用mutation就可以了。

听起来是不是很绕?不过你不用担心,下面的代码就很清晰地演示了这个过程。

首先,我们在createStore的配置中,新增了actions配置,这个配置中所有的函数,可以通过解构获得commit函数。内部的异步任务完成后,就随时可以调用commit来执行mutations去更新数据。

代码语言:js
AI代码解释
复制
const store = createStore({
  state () {
    return {
      count: 666
    }
  },
  ...
  actions:{
      asyncAdd({commit}){
          setTimeout(()=>{
            commit('add')
          },1000)
      }
  }
})

action并不是直接修改数据,而是通过mutations去修改,这是我提醒你需要注意的。actions的调用方式是使用store.dispatch,在下面的代码中你可以看到这样的变化效果:页面中新增了一个asyncAdd的按钮,点击后会延迟一秒做累加。

代码语言:js
AI代码解释
复制
function asyncAdd(){
    store.dispatch('asyncAdd')
}

执行效果:

Vuex在整体上的逻辑如下图所示,从宏观来说,Vue的组件负责渲染页面,组件中用到跨页面的数据,就是用state来存储,但是Vue不能直接修改state,而是要通过actions/mutations去做数据的修改。

Vuex官方结构图,拆解了Vuex在Vue全家桶中的定位,项目也用Vuex管理所有的跨组件的数据,并且我们也会在Vuex内部根据功能模块去做拆分,会把用户、权限等不同模块的组件分开去管理。

由于Vuex所有的数据修改都是通过mutations来完成的,因而我们可以很方便地监控到数据的动态变化,后面我们可以借助官方的调试工具,非常方便地去调试项目中的数据变化。

回到正在做的这个项目中,有大量的数据交互需求、用户的登录状态、登录的有效期、布局的设置,不同用户还会有不同的菜单权限等。

不过面对眼花缭乱的交互需求,你不能自乱阵脚。总体来说, 我们在决定一个数据是否用Vuex来管理的时候,核心就是要思考清楚,这个数据是否有共享给其他页面或者是其他组件的需要。如果需要,就放置在Vuex中管理;如果不需要,就应该放在组件内部使用ref或者reactive去管理。

6 下一代Vuex

Vuex由于在API的设计上,对TypeScript的类型推导的支持比较复杂,用起来很是痛苦。因为我们的项目一直用的都是JavaScript,你可能感触并不深,但对于使用TypeScript的用户来说,Vuex的这种问题是很明显的。

为了解决Vuex的这个问题,Vuex的作者最近发布了一个新的作品叫Pinia,并将其称之为下一代的Vuex。Pinia的API的设计非常接近Vuex5的提案,首先,Pinia不需要Vuex自定义复杂的类型去支持TypeScript,天生对类型推断就非常友好,并且对Vue Devtool的支持也非常好,是一个很有潜力的状态管理框架。

7 总结

从前端数据管理概念开始讲起。每个组件内部有自己的数据和模板,那共享的数据怎么科学管理呢?这就需要Vuex。

Vuex是一个状态和数据管理的框架,负责管理项目中多个组件和多个页面共享的数据。在开发项目的时候,我们就会把数据分成两个部分,一种数据是在某个组件内部使用,我们使用ref或者reactive定义即可,另外一种数据需要跨页面共享,就需要使用Vuex来进行管理。

之后,我们还讲到了Vuex带来了几个新的概念,我们使用state定义数据,使用mutation定义修改数据的逻辑,并且在组件中使用commit去调用mutations。在此基础之上,还可以用getters去实现Vuex世界的计算属性,使用action来去定义异步任务,并且在内部调用mutation去同步数据。

Vuex的出现,让我们整个项目中的数据流动变得非常自然。数据流向组件,但组件不能直接修改数据,而是要通过mutation提出申请,mutation去修改数据,形成了一个圆环。这种方式对于我们项目的开发、维护和调试都是有很大的帮助。之后,我们一起手写了一个迷你的Vuex,通过实战巩固前面的学习。

最后,简单介绍Pinia框架,即下一代Vuex。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Clickhouse 迁移到 Doris 的最佳实践
在将数据从 Clickhouse 迁移到 Apache Doris / SelectDB Cloud 的过程中,涉及表结构迁移、查询语句迁移以及数据迁移等多个关键环节。每个环节都有其复杂性和需要注意的细节,本文将详细介绍这些内容及对应的最佳实践方法。
数据极客圈
2025/07/14
1840
Clickhouse 迁移到 Doris 的最佳实践
6亿数据秒级查询,ClickHouse太快了!
ClickHouse 在数据分析技术领域早已声名远扬,最近由于项目需求使用到了 ClickHouse 做分析数据库,于是用测试环境做了一个单表 6 亿数据量的性能测试。
架构师修炼
2021/04/12
2.9K1
【TBase开源版测评】Hello, TBase
TBase是腾讯基于PostgreSQL研发的一个分布式HTAP数据库,适用于拥有海量数据、高并发、部分分析场景解决,以及分布式事务能力的应用场景。 从现有的资料来看,TBase本身在腾讯内部是经过业务长期迭代打磨的产品,目前在腾讯云上也提供商业化的版本销售。
HappenLee
2020/08/28
2.7K1
【TBase开源版测评】Hello, TBase
【天穹OS】虚拟表:支持极速查询的下一代湖仓一体新范式
湖仓一体(Lakehouse)是近年来比较火的大数据概念,它将数据湖(Data Lake)和数据仓库(Data Warehouse)的优势结合起来,为企业提供了更强大、更灵活的数据管理解决方案。Gartner 技术曲线的描绘中,Lakehouse是一项非常重要技术,预计还有2~5年进入平台期,国内是5~10年。
jhonye
2023/10/31
2K0
Palo Doris不会用?(基础指南)
在快速入门教程中,我们通过 Doris 的 UI 界面完成了 Doris 的一些基本操作。而在实际生产环境中,用户通常需要使用程序连接到 Doris 并进行各种操作。
857技术社区
2022/05/17
6160
悄悄学习Doris,偷偷惊艳所有人 | Apache Doris四万字小总结
DorisDB是由Apache Doris核心研发团队打造的新一代企业级MPP数据库。它继承了Apache Doris项目十多年研发成果,累积了线上数千台服务器稳定运行经验,并在此基础上,对传统MPP数据库进行了开创性的革新。
王知无-import_bigdata
2021/09/22
8.4K1
5分钟在k8s上可视化搭建Kylin5
需要有一个可用的Kubernetes环境,如果不会搭建可以搜索引擎检索kubekey或者rancher,有大量快速搭建文档,当然用kubeadmin也可以。
CloudEon开源
2023/06/05
8730
5分钟在k8s上可视化搭建Kylin5
Pilosa使用入门
Pilosa是一款开源的分布式索引,主要是为了查询速度和水平伸缩性而设计的。如果数据规模在数十亿,并且有上百万的属性值,那么就可以考虑使用Pilosa解决这些问题:哪些属性最常见?哪些数据对象拥有特定的某些属性?哪些属性组会经常一起出现?等等类似的问题。
skyyws
2022/05/20
8020
Pilosa使用入门
数据无界、湖仓无界,Apache Doris 湖仓一体典型场景实战指南(下篇)
在数据驱动决策的时代,湖仓一体架构以统一存储、统一计算、统一管理的创新形式,补齐了传统数据仓库和数据湖的短板,逐步成为企业大数据解决方案新的标准。
SelectDB技术团队
2025/02/21
5220
ClickHouse S3 Engine 数量级调优
本文主要讲解 ClickHouse S3 Engine 的读取写入性能代码 及 数量级调优
jasong
2022/03/08
9862
Iceberg 实践 | B 站通过数据组织加速大规模数据分析
交互式分析是大数据分析的一个重要方向,基于TB甚至PB量级的数据数据为用户提供秒级甚至亚秒级的交互式分析体验,能够大大提升数据分析人员的工作效率和使用体验。限于机器的物理资源限制,对于超大规模的数据的全表扫描以及全表计算自然无法实现交互式的响应,但是在大数据分析的典型场景中,多维分析一般都会带有过滤条件,对于这种类型的查询,尤其是在高基数字段上的过滤查询,理论上可以在读取数据的时候跳过所有不相关的数据,只读取极少部分需要的数据,这种技术一般称为Data Clustering以及Data Skipping。Data Clustering是指数据按照读取时的IO粒度紧密聚集,而Data Skipping则根据过滤条件在读取时跳过不相干的数据,Data Clustering的方式以及查询中的过滤条件共同决定了Data Skipping的效果,从而影响查询的响应时间,对于TB甚至PB级别的数据,如何通过Data Clustering以及Data Skipping技术高效的跳过所有逻辑上不需要的数据,是能否实现交互式分析的体验的关键因素之一。
大数据技术架构
2021/03/23
2.4K0
Shopee ClickHouse 冷热数据分离存储架构与实践
ClickHouse 是一款开源的列存 OLAP(在线分析查询)型数据库,实现了向量化执行引擎,具有优秀的 AP 查询性能。Shopee ClickHouse 则是基于 ClickHouse 持续做二次迭代开发和产品架构演进的分析型数据库。
Shopee技术团队
2021/10/21
1.7K0
Shopee ClickHouse 冷热数据分离存储架构与实践
硬刚Doris系列」Apache Doris基本使用和数据模型
我们使用 event_day 列作为分区列,建立3个分区: p201706, p201707, p201708
王知无-import_bigdata
2022/06/05
3K0
硬刚Doris系列」Apache Doris基本使用和数据模型
全面介绍 Apache Doris 数据灾备恢复机制及使用示例
Apache Doris 作为一款 OLAP 实时数据仓库,在越来越多的中大型企业中逐步占据着主数仓这样的重要位置,主数仓不同于 OLAP 查询引擎的场景定位,对于数据的灾备恢复机制有比较高的要求,本篇就让我们全面的介绍和示范如何利用这些特性能力构建集群数据的灾备恢复机制。
苏奕嘉
2025/07/14
1610
全面介绍 Apache Doris 数据灾备恢复机制及使用示例
深入并行:从生产者到消费者模型深度理解Oracle的并行
陈焕生 Oracle Real-World Performance Group 成员,senior performance engineer,专注于 OLTP、OLAP 系统 在 Exadata 平台和 In-Memory 特性上的最佳实践。个人博客 http://dbsid.com 。 Oracle 的并行执行 Oracle 的并行执行是一种分而治之的方法. 执行一个 SQL 时, 分配多个并行进程同时执行数据扫描,连接以及聚合等操作, 使用更多的资源, 得到更快的 SQL 响应时间。并行执行是充分
数据和云
2018/03/06
1.5K0
深入并行:从生产者到消费者模型深度理解Oracle的并行
一文了解ClickHouse
ClickHouse是Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快100-1000倍。ClickHouse的性能超过了目前市场上可比的面向列的DBMS,每秒钟每台服务器每秒处理数亿至十亿多行和数十千兆字节的数据。
用户1278550
2020/05/15
1.5K0
弃用 MySQL 后存储成本降低 85%,携程业务系统数据库升级技术实践
携程是一家中国领先的在线票务服务公司,从 1999 年创立至今,数据库系统历经三次替换。在移动互联网时代,面对云计算卷积而来的海量数据,携程通过新的数据库方案实现存储成本降低 85% 左右,性能提升数倍。本文讲述携程在历史库场景下,如何解决水平扩容、存储成本、导入性能等痛点,以及对于解决方案的制定和思考过程。
深度学习与Python
2023/09/18
4210
弃用 MySQL 后存储成本降低 85%,携程业务系统数据库升级技术实践
五分钟了解Palo Doris的索引原理及应用场景!
目前 Doris 主要支持两类索引:内建的智能索引,包括前缀索引和ZoneMap索引。用户创建的二级索引,包括Bloom Filter索引和Bitmap倒排索引。
857技术社区
2022/05/17
1.1K0
兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
在现代化的数据分析场景中,数据量以指数级速度快速膨胀,分析维度在不断扩展,查询逻辑的复杂度也在日益增加。从性能角度考虑,在承担高并发查询的压力下,秒级别甚至更快的响应速度已成为基本需求。同时,面对有限的计算资源,成本及性能如何平衡,严格的资源管控也显得尤为重要。
SelectDB技术团队
2024/11/20
5200
TiDB 7.4 发版:正式兼容 MySQL 8.0
MySQL 是全球最受欢迎的开源数据库,长期位于 DB-Engines Ranking 排行榜第二名,在世界范围内拥有数量庞大的企业用户和开发者。然而,随着时间的推移,MySQL 用户正面临新挑战。Oracle 官宣将在 2023 年 10 月终止 MySQL 5.7 版本的官方技术支持。据第三方统计显示,目前仍有超过一半的 MySQL 服务器运行在 5.7 版本。在未来几个月,大量的 MySQL 实例必须升级至 8.0 及更高版本,否则将无法享受 Oracle 提供的技术支持和重要补丁更新,企业级用户将面临重大考验。
PingCAP
2023/10/30
4680
推荐阅读
相关推荐
Clickhouse 迁移到 Doris 的最佳实践
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档