VUE3:深入浅出探究pinia、provide\inject在多层组件页面是怎么使用的
·
1. Pinia 状态管理
什么是 Pinia?
Pinia 是 Vue 3 官方推荐的状态管理库,可以理解为一个"全局数据仓库"。
为什么需要它? 想象一下,你有一个购物车数据,需要在多个页面(首页、商品页、结算页)共享。如果用 props 一层层传递会很麻烦,Pinia 就是解决这个问题的。
安装 Pinia
npm install pinia
使用步骤
第一步:在 main.js 中注册 Pinia
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
第二步:创建 Store(数据仓库)
// src/stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// defineStore 第一个参数是 store 的唯一标识(名字)
export const useCounterStore = defineStore('counter', () => {
// ========== state(数据)==========
// 就像组件中的 ref,存放响应式数据
const count = ref(0)
const name = ref('小明')
// ========== getters(计算属性)==========
// 就像组件中的 computed
const doubleCount = computed(() => count.value * 2)
// ========== actions(方法)==========
// 修改数据的方法
function increment() {
count.value++
}
function decrement() {
count.value--
}
// 异步操作也可以放在 actions 中
async function asyncIncrement() {
// 模拟接口请求
await new Promise(resolve => setTimeout(resolve, 1000))
count.value++
}
// 必须 return 出去才能在组件中使用
return {
count,
name,
doubleCount,
increment,
decrement,
asyncIncrement
}
})
第三步:在组件中使用 Store
爷爷组件 (GrandParent.vue)
<template>
<div class="grandparent">
<h2>👴 爷爷组件</h2>
<p>从 Pinia 获取的 count: {{ counterStore.count }}</p>
<p>双倍 count: {{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">爷爷点击 +1</button>
<!-- 嵌套父组件 -->
<ParentComponent />
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
import ParentComponent from './ParentComponent.vue'
// 获取 store 实例,直接使用即可
const counterStore = useCounterStore()
</script>
父组件 (ParentComponent.vue)
<template>
<div class="parent">
<h3>👨 父组件</h3>
<p>从 Pinia 获取的 count: {{ counterStore.count }}</p>
<button @click="counterStore.decrement">父亲点击 -1</button>
<!-- 嵌套子组件 -->
<ChildComponent />
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
import ChildComponent from './ChildComponent.vue'
// 任何组件都可以直接获取同一个 store
const counterStore = useCounterStore()
</script>
孙子组件 (ChildComponent.vue)
<template>
<div class="child">
<h4>👶 孙子组件</h4>
<p>从 Pinia 获取的 count: {{ counterStore.count }}</p>
<button @click="counterStore.asyncIncrement">孙子异步 +1(1秒后)</button>
</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
// 三层组件都能访问同一份数据!
const counterStore = useCounterStore()
</script>
Pinia 小结
|
概念 |
说明 |
|
Store |
全局数据仓库,所有组件都能访问 |
|
State |
存放数据的地方(用 ref 或 reactive) |
|
Getters |
计算属性(用 computed) |
|
Actions |
修改数据的方法(普通函数,可以是异步的) |
2. Provide / Inject 依赖注入
什么是 Provide / Inject?
这是 Vue 内置的"跨层级传值"方案,不需要安装额外的库。
- provide(提供):祖先组件提供数据
- inject(注入):后代组件接收数据
与 Pinia 的区别: Provide/Inject 的数据是"局部的",只在当前组件树内共享;而 Pinia 是"全局的"。
基本使用
爷爷组件(提供数据)
<template>
<div class="grandparent">
<h2>👴 爷爷组件 - Provide 示例</h2>
<p>爷爷的消息: {{ message }}</p>
<p>爷爷的主题色: {{ theme }}</p>
<button @click="changeTheme">切换主题</button>
<ParentProvide />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ParentProvide from './ParentProvide.vue'
// ========== 定义要共享的数据 ==========
const message = ref('来自爷爷的问候~')
const theme = ref('light')
// 切换主题的方法
const changeTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
// ========== 使用 provide 提供数据 ==========
// provide(key, value)
// key: 字符串或 Symbol,用于标识
// value: 要共享的数据
// 提供普通数据
provide('message', message)
// 提供响应式数据(子组件能感知变化)
provide('theme', theme)
// 提供方法(让子组件也能修改数据)
provide('changeTheme', changeTheme)
// 提供对象(包含多个相关数据)
provide('userInfo', {
name: '张三',
age: 60,
role: 'grandparent'
})
</script>
父组件(中间层,不需要处理)
<template>
<div class="parent">
<h3>👨 父组件</h3>
<p>我是中间层,不需要 inject,数据可以跳过我直接传给孙子</p>
<ChildInject />
</div>
</template>
<script setup>
import ChildInject from './ChildInject.vue'
// 注意:父组件不需要做任何事,数据会"穿透"过去
</script>
孙子组件(注入数据)
<template>
<div class="child" :class="theme">
<h4>👶 孙子组件 - Inject 示例</h4>
<!-- 使用注入的数据 -->
<p>收到爷爷的消息: {{ message }}</p>
<p>当前主题: {{ theme }}</p>
<p>用户信息: {{ userInfo.name }}, {{ userInfo.age }}岁</p>
<!-- 调用爷爷提供的方法 -->
<button @click="changeTheme">孙子也能切换主题</button>
</div>
</template>
<script setup>
import { inject } from 'vue'
// ========== 使用 inject 接收数据 ==========
// inject(key) 或 inject(key, 默认值)
// 接收响应式数据
const message = inject('message')
const theme = inject('theme')
// 接收方法
const changeTheme = inject('changeTheme')
// 接收对象
const userInfo = inject('userInfo')
// 设置默认值(如果祖先没有 provide,就用默认值)
const notExist = inject('notExist', '这是默认值')
</script>
<style scoped>
.light {
background: #fff;
color: #333;
}
.dark {
background: #333;
color: #fff;
}
</style>
Provide / Inject 小结
|
特点 |
说明 |
|
适用场景 |
跨多层组件传值,避免 props 层层传递 |
|
响应式 |
如果 provide 的是 ref/reactive,子组件能响应变化 |
|
作用范围 |
只在当前组件树内有效 |
|
与 Pinia 对比 |
更轻量,但没有 Pinia 的状态管理能力 |
更多推荐


所有评论(0)