useAppStore
import { useAppStore } from '@/stores/AppSetting';
const appStore = useAppStore();
useAppStore是一个Pinia定义的store函数,为什么我们通常写成:
const appStore = useAppStore();
而不是每次都调用useAppStore():
useAppStore()是一个“获取入口函数”
useAppStore() 本身并不是一个全局对象,而是一个返回 store 实例的函数。
当你调用它时:
- 它会去Pinia的“全局容器”里查找(或创建)对应的store实例
- 然后返回这个store对象(带有state,getters,actions)
所以const appStore = useAppStore();就是在当前作用域中拿到这个store实例
- 为了防止反复调用而浪费的性能
虽然Pinia会缓存返回的实例(不会创建多个store),但:
- 每次调用都需要重新执行函数;
- 可读性和效率都不好;
- 一旦 store 需要注入依赖(比如不同 pinia 实例),可能还会出问题。
Pinia
Vue3的官方状态库,是Vuex的继任者
Pinia是什么
Pinia 是一个用来在 Vue 应用中**集中管理状态(state)**的库。
也就是说,Pinia负责存放那些:
- 不适合放在单个组件里的数据
- 需要在多个组件之间共享的数据
- 或者需要全局保存的设置、登录状态、主题等
简单来说:组件自己的数据放在data()或ref()里,而多个组件要共享的数据放在Pinia里
类似Java中的全局变量:
public class GlobalState {
public static boolean sidebarOpened = true;
}
在程序运行期间,任何类都可以通过 GlobalState.sidebarOpened 访问或修改这个变量。
Pinia 的 store 其实也类似:
import { useAppStore } from '@/stores/AppSetting'
const appStore = useAppStore()
appStore.sidebar.opened = true
无论你在 Vue 的哪个组件中调用 useAppStore(),拿到的都是同一个 store 实例。
这就像 Java 里所有类访问的都是同一个 GlobalState。
不同点是前端应用不像Java服务那样“一直运行”,浏览器页面一旦刷新,JavaScript代码就会:
- 全部重新加载
- 所有内存(包括Pinia的store)都会被清空
- 重新执行Vue应用的初始逻辑
因此,Pinia 中的数据虽然在页面运行期间是全局共享的,但刷新后就会丢失。
除非你手动把数据持久化到 localStorage、sessionStorage 或 IndexedDB 中
为什么要用Pinia?
在一个小项目中,每个组件都有自己的状态还没有问题
但在大项目里,会出现:
- A组件修改数据 -> B组件跟着更新
- C组件需要知道登录状态
- D组件要控制全局主题颜色
如果这些数据分散在组件里,会很乱。这时候就需要一个全局数据中心,让所有组件都能访问和修改。
Pinia就是这个中心:
ComponentA 改变状态 -> Pinia Store 保存状态 -> ComponentB 自动更新
Pinia的核心概念
Pinia有三个主要部分:
| 名称 | 作用 | 类比 |
|---|---|---|
| state | 存储数据 | Vue 的 data() |
| getters | 计算属性 | Vue 的 computed() |
| actions | 修改数据的方法 | Vue 的 methods() |
示例:
// src/stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});
函数体:
- defindStore: Pinia的核心函数,用来定义一个全局store
- counter: 是Store的唯一ID(就像变量名),在调试或插件中用来区分不同的store
- useCounterStore: 是导出的函数名,组件中调用它就能拿到store实例
函数内部:
state
state: () => ({
count: 0,
})
- 相当于Vue组件的data()
- 用来定义全局的响应式状态
- count初始值为0
counter.count // 访问状态
counter.count++ // 修改状态
Pinia 会自动把 state 包装成响应式的(用 Vue 的 reactive() 实现)。
getter
getters: {
double: (state) => state.count * 2,
}
- 类似vue的computed计算属性
- 不直接修改状态,而是根据state计算出一个衍生值
- 当count改变时,double会自动更新
actions
actions: {
increment() {
this.count++;
},
}
- actions:就像vue组件里的methods
- 可以执行逻辑(同步或异步)并修改state
- 在这里,this就指向当前store实例
<script setup>
import { useCounterStore } from '@/stores/counter';
const counter = useCounterStore();
</script>
<template>
<div>
<p>当前计数:{{ counter.count }}</p>
<p>两倍值:{{ counter.double }}</p>
<button @click="counter.increment">+1</button>
</div>
</template>
组合式写法
如果你更习惯 Vue 3 的 ref / reactive 写法,也可以这么写
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// state:定义响应式变量
const count = ref(0)
// getters:定义计算属性
const double = computed(() => count.value * 2)
// actions:定义操作函数
function increment() {
count.value++
}
// 返回要暴露给外部使用的属性和方法
return { count, double, increment }
})