Vue3 完整学习笔记 - 第四部分
4. Pinia 状态管理与组件通信
4.1 Pinia 基础
重点掌握:
- Store 的创建和使用
- State、Getters、Actions 的定义
- 组合式风格的 Store
基础 Store 示例:
// stores/counter.ts
import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', {// 状态state: () => ({count: 0,name: 'Eduardo',items: []}),// 计算属性getters: {doubleCount: (state) => state.count * 2,// 使用this访问其他getterdoubleCountPlusOne(): number {return this.doubleCount + 1}},// 方法actions: {increment() {this.count++},async fetchItems() {const response = await fetch('/api/items')this.items = await response.json()}}
})// 组合式写法
export const useCounterStore = defineStore('counter', () => {const count = ref(0)const name = ref('Eduardo')const doubleCount = computed(() => count.value * 2)function increment() {count.value++}return { count, name, doubleCount, increment }
})
在组件中使用:
<template><div><p>Count: {{ counter.count }}</p><p>Double: {{ counter.doubleCount }}</p><button @click="counter.increment">+1</button><button @click="increment">+1 (Destructured)</button></div>
</template><script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'const counter = useCounterStore()// 解构时保持响应性
const { count, doubleCount } = storeToRefs(counter)
const { increment } = counter// 批量修改状态
function updateState() {counter.$patch({count: counter.count + 1,name: 'John'})// 或者使用函数形式counter.$patch((state) => {state.count++state.name = 'John'})
}// 监听状态变化
counter.$subscribe((mutation, state) => {console.log(mutation.type, mutation.payload)
})
</script>
4.2 Pinia 进阶使用
重点掌握:
- 插件开发
- 持久化存储
- 状态重置
示例代码:
// stores/plugins/persistence.ts
import { PiniaPluginContext } from 'pinia'export function persistencePlugin({ store }: PiniaPluginContext) {// 从localStorage恢复状态const savedState = localStorage.getItem(`${store.$id}-state`)if (savedState) {store.$state = JSON.parse(savedState)}// 监听状态变化并保存store.$subscribe(({ storeId, state }) => {localStorage.setItem(`${storeId}-state`, JSON.stringify(state))})
}// main.ts
const pinia = createPinia()
pinia.use(persistencePlugin)// 自定义 Store 属性
pinia.use(({ store }) => {store.customProperty = 'my custom value'store.customMethod = () => console.log('hello')
})
4.3 组件通信方式一:Props 与 Emit
重点掌握:
- Props 定义与验证
- 事件发射与监听
- v-model 的使用
示例代码:
<!-- ChildComponent.vue -->
<template><div><input:value="modelValue"@input="$emit('update:modelValue', $event.target.value)"/><button @click="handleClick">Click Me</button></div>
</template><script setup lang="ts">
const props = defineProps<{modelValue: stringlabel?: string
}>()const emit = defineEmits<{(e: 'update:modelValue', value: string): void(e: 'customEvent', { id: number, value: string }): void
}>()const handleClick = () => {emit('customEvent', { id: 1, value: 'test' })
}
</script><!-- ParentComponent.vue -->
<template><div><ChildComponentv-model="inputValue"label="Username"@customEvent="handleCustomEvent"/></div>
</template><script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'const inputValue = ref('')const handleCustomEvent = (data) => {console.log('Custom event received:', data)
}
</script>
4.4 组件通信方式二:Provide/Inject
重点掌握:
- 跨层级组件通信
- 响应式数据传递
- 作用域插槽替代方案
示例代码:
<!-- ParentComponent.vue -->
<template><div><slot :data="data" :updateData="updateData"></slot><ChildComponent /></div>
</template><script setup>
import { provide, ref } from 'vue'const data = ref({ count: 0 })// 提供响应式数据
provide('data', data)// 提供方法
const updateData = () => {data.value.count++
}
provide('updateData', updateData)// 提供只读数据
provide('readonlyData', readonly(data))
</script><!-- ChildComponent.vue -->
<template><div><p>Count: {{ data.count }}</p><button @click="updateData">Update</button></div>
</template><script setup>
import { inject } from 'vue'// 注入数据和方法
const data = inject('data')
const updateData = inject('updateData')// 使用默认值
const theme = inject('theme', 'light')
</script>
4.5 组件通信方式三:Event Bus (mitt)
重点掌握:
- 事件总线的使用
- 事件监听与清理
- 适用场景
示例代码:
// eventBus.ts
import mitt from 'mitt'export const emitter = mitt()// 类型定义
type Events = {'item-click': { id: number; data: any }'data-updated': void
}export const typedEmitter = mitt<Events>()// ComponentA.vue
<script setup>
import { onMounted, onUnmounted } from 'vue'
import { emitter } from './eventBus'// 监听事件
onMounted(() => {emitter.on('item-click', (event) => {console.log('Item clicked:', event)})
})// 清理事件监听
onUnmounted(() => {emitter.off('item-click')
})// 发送事件
const handleClick = () => {emitter.emit('item-click', { id: 1, 'test' })
}
</script>// ComponentB.vue
<script setup>
import { emitter } from './eventBus'// 发送事件
const notifyUpdate = () => {emitter.emit('data-updated')
}
</script>
4.6 异步组件与动态组件
重点掌握:
- 异步组件的加载
- 动态组件切换
- 加载状态处理
示例代码:
<template><div><!-- 异步组件 --><Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template></Suspense><!-- 动态组件 --><component :is="currentComponent"@change="handleChange"/><button @click="toggleComponent">Switch Component</button></div>
</template><script setup>
import { ref, defineAsyncComponent } from 'vue'// 异步组件定义
const AsyncComponent = defineAsyncComponent({loader: () => import('./HeavyComponent.vue'),loadingComponent: LoadingComponent,errorComponent: ErrorComponent,delay: 200,timeout: 3000
})// 动态组件
const components = {'comp-a': defineAsyncComponent(() => import('./ComponentA.vue')),'comp-b': defineAsyncComponent(() => import('./ComponentB.vue'))
}const currentComponent = ref('comp-a')const toggleComponent = () => {currentComponent.value = currentComponent.value === 'comp-a' ? 'comp-b' : 'comp-a'
}
</script>