状态提升:前端开发中的状态管理的设计思想

在前端开发中,我们几乎绕不开一个核心问题:状态(state)该放在哪里?

随着项目复杂度的提升,状态的存放位置也会经历一次次"升级":

子组件 → 父组件 → Hook(组合式函数)→ Pinia(全局状态管理)

这篇文章,我会带你一步步拆解这个"状态提升"的演进过程,并结合VUE代码示例,帮你理解每一次升级背后的动机和设计思想。


一、第一阶段:状态在子组件中(局部状态)

在项目早期,我们通常会把状态直接写在子组件内部。

示例:一个计数器组件

xml 复制代码
<!-- Counter.vue -->
<script setup>
import { ref } from 'vue'

const count = ref(0)

const increment = () => {
  count.value++
}
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

特点

  • 状态封装在组件内部
  • 简单直观
  • 适合完全独立的 UI 组件

问题

如果有两个组件都需要用到这个 count 呢?

比如:

xml 复制代码
<Counter />
<Display />

Display 组件也想显示这个 count,怎么办?

这时我们就需要第一次升级。


二、第二阶段:从子组件提升到父组件

当多个子组件共享状态时,我们会把状态"提升"到它们的共同父组件。

这和 React 的"状态提升"思想是一致的。

父组件管理状态

xml 复制代码
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue'
import Counter from './Counter.vue'
import Display from './Display.vue'

const count = ref(0)

const increment = () => {
  count.value++
}
</script>

<template>
  <Counter :count="count" @increment="increment" />
  <Display :count="count" />
</template>

子组件只负责展示和触发

xml 复制代码
<!-- Counter.vue -->
<script setup>
defineProps({
  count: Number
})

defineEmits(['increment'])
</script>

<template>
  <button @click="$emit('increment')">+1</button>
</template>

优点

  • 状态集中管理
  • 数据流清晰(单向数据流)

缺点

  • 层级一深就会出现:

    • props drilling(层层传参)
    • 事件层层冒泡
  • 父组件变得"臃肿"

当项目规模扩大后,这种方式开始吃力。

于是我们进行第二次升级。


三、第三阶段:从父组件提升到 Hook(组合式函数)

在 Vue 3 中,Composition API 让我们可以把逻辑抽离成 Hook(组合式函数)。

我们把状态抽离到一个独立文件中。

创建一个 useCounter.ts

javascript 复制代码
// useCounter.ts
import { ref } from 'vue'

export function useCounter() {
  const count = ref(0)

  const increment = () => {
    count.value++
  }

  return {
    count,
    increment
  }
}

在组件中使用

xml 复制代码
<script setup>
import { useCounter } from './useCounter'

const { count, increment } = useCounter()
</script>

优点

  • 逻辑复用
  • 代码结构更清晰
  • 组件变"干净"
  • 可测试性更强

但问题来了

如果两个组件都调用 useCounter()

ini 复制代码
const a = useCounter()
const b = useCounter()

它们的 count 是:

❌ 不共享的

每调用一次都会创建新的状态实例。

如果我们希望多个组件共享同一个状态怎么办?

这时候,Hook 已经不够用了。

于是我们迎来终极升级。


四、第四阶段:从 Hook 升级到 Pinia

当状态需要在多个页面、多个模块、多个层级中共享时,我们就需要真正的状态管理工具。

在 Vue 生态中,主流选择是:

  • Vuex(旧)
  • Pinia(官方推荐)

这里我们使用 Pinia。


什么是 Pinia?

Pinia 是 Vue 官方推荐的状态管理库,支持 Vue 3,API 设计非常现代化。

它的理念是:

Store = 可复用的全局 Hook


创建一个 Counter Store

javascript 复制代码
// stores/counter.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)

  const increment = () => {
    count.value++
  }

  return { count, increment }
})

在组件中使用

xml 复制代码
<script setup>
import { useCounterStore } from '@/stores/counter'

const counter = useCounterStore()
</script>

<template>
  <button @click="counter.increment">
    {{ counter.count }}
  </button>
</template>

关键特性

  • 所有组件共享同一个 store
  • 自动响应式
  • DevTools 支持
  • 模块化管理

状态升级的本质

我们来总结一下这四个阶段:

阶段 状态位置 适用场景 缺点
子组件 组件内部 完全独立组件 无法共享
父组件 父级 局部共享 层级深会混乱
Hook 逻辑抽离 逻辑复用 默认不共享
Pinia 全局 Store 跨页面共享 增加架构复杂度

设计哲学:状态放在哪里?

可以用一句话概括:

状态应该放在"刚好需要它的最上层"

  • 只一个组件用 → 放子组件
  • 两个兄弟组件用 → 放父组件
  • 多个地方用但不共享 → Hook
  • 全局共享 → Pinia

这是一种"按需升级"的架构策略。


不要一开始就上 Pinia

不要直接提升到pinia,这会带来:

  • 不必要的全局耦合
  • 难以维护
  • 状态污染

记住:

全局状态是一种"权力",不要滥用。

应该从下至上,找到最合适的地方,随着需求的变更,代码也跟随变更。


架构升级的思维模型

这个升级过程,本质上体现的是:

  • 局部化 → 抽象化 → 全局化
  • 组件驱动 → 逻辑驱动 → 状态驱动

这也是现代前端架构演进的核心路线。


结语

Vue 的状态管理不是非黑即白的选择,而是一个"渐进增强"的过程。

当你理解了:

  • 为什么提升状态
  • 什么时候该提升
  • 提升的边界在哪里

你就真正掌握了 Vue 状态管理的设计思想。

思考

  • 如果所有的父子组件都需要这个状态呢?(Provide/Inject)
相关推荐
酒鼎10 分钟前
学习笔记(12-02)事件循环 - 实战案例 —⭐
前端·javascript
Bigger15 分钟前
第一章:我是如何剖析 Claude Code 整体架构与启动流程的
前端·aigc·claude
竹林81821 分钟前
从“连接失败”到丝滑登录:我用 ethers.js v6 搞定 MetaMask 钱包连接的全过程
前端·javascript
oi..25 分钟前
《Web 安全入门|XSS 漏洞原理、CSP 策略与 HttpOnly 防护实践》
前端·网络·测试工具·安全·web安全·xss
UXbot34 分钟前
2026年AI全链路产品开发工具对比:5款从创意到上线一站式平台深度解析
前端·ui·kotlin·软件构建·swift·原型模式
一拳不是超人44 分钟前
前端工程师也要懂的服务器部署知识:从 Nginx 到 CI/CD
服务器·前端
AlkaidSTART1 小时前
TanStack Query 技术指南:异步状态管理核心实践
前端·react.js
三原1 小时前
超级好用的三原后台管理v1.0.0发布🎉(Vue3 + Ant Design Vue + Java Spring Boot )附源码
java·vue.js·开源
之歆1 小时前
RBAC权限模型设计与实现深度解析
vue.js
种花家的强总1 小时前
前端项目开发/维护中降低成本的方式之一:降低耦合度
前端