Pinia的安装,使用,与情景教学
#本文基于Vue3和ts去实现Pinia的使用与安装,Pinia用法为组合式API,情景教学为一个基于全局的举报框的pinia逻辑#
1,下载pinia
这里推荐pnpm下载
pnpm add pinia
2,在main.ts中注册并挂载pinia
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia' // 导入 Pinia
import App from './App.vue'
const app = createApp(App)
// 创建 Pinia 实例
const pinia = createPinia()
// 挂载 Pinia
app.use(pinia)
app.mount('#app')
3,创造一个Store
- 引入defineStore方法
- 使用 defineStore 定义 store,推荐store的命名方式为use+模块+Store
- 其他语法与vue3一致
- 最后别忘了return导出
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
return { count, doubleCount, increment }
})
4, 在组件中使用store
- 引入对应单例
- 引入storeToRefs保持响应式解构
- 剩下的像ref变量一样直接使用即可
<!-- Component.vue -->
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia' // 保持响应式解构
const counterStore = useCounterStore()
// 直接解构会失去响应式,需要用 storeToRefs
const { count, doubleCount } = storeToRefs(counterStore)
const { increment } = counterStore
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="counterStore.reset()">Reset</button>
</div>
</template>
5,项目的目录结构建议
- 可以在src下单独开一个stores用来存放所有的仓库
- 可以创造index.ts来统一导出所有的store
// stores/index.ts示例
export * from './counter'
export * from './user'
情景教学
#你需要实现一个举报框,能在不同页面弹出,如评论区的举报,视频页的举报,帖子的举报。如果在每个帖子中引用,会导致剧烈的堆积。如果在大页面引用,被举报者的id等数据的传递又即为麻烦。是时候用pinia了#
1,创建reportStore.ts
// 举报框所用数据
import {defineStore} from 'pinia'
import {ref} from 'vue'
export const useReportStore = defineStore('report', () => {
})
2, 创建举报所需的所有状态
- 举报框的显示的bool变量
- 举报方相关信息
- 举报的描述文本
- 举报原因(源自多选框)
// 控制举报对话框的显示状态
const isReportDialogVisible = ref<boolean>(false)
// 存储被举报被人的相关信息
const reportTarget = ref<{
id: string | number,
type: 'post' | 'commment' | 'user' | string;
title?: string;
} | null>(null)
// 举报描述文本
const reportDescription = ref<string>('')
// 举报原因
const reportReasons = ref<string[]>([])
3,封装打开对话函数
- 这一步成功实现跨组件传递数据
- 携带举报ID等所有信息
// 打开举报对话框
const openReportDialog = (target:{
id: string | number;
type: 'post' | 'comment' | 'user' | string;
title?: string
}) => {
reportTarget.value = target
isReportDialogVisible.value = true
reportDescription.value = '' // 清空之前的举报描述
reportReasons.value = [] // 清空举报原因
}
4,封装关闭函数与提交举报函数
需注意:提交函数返回一个数组对象,提供给父组件组做消息提醒,让父组件做最后的结果逻辑处理
// 关闭举报对话框
const closeReportDialog = () => {
isReportDialogVisible.value = false
}
// 提交举报
const submitReport = async (): Promise<{ success: boolean; message: string }> => {
if (!reportReasons.value.length) {
return {
success: false,
message: '请至少选择一个举报原因'
}
}
if (!reportDescription.value.trim()) {
return {
success: false,
message: '请填写举报描述'
}
}
try {
// 调用实际的API
console.log('举报信息:', {
targetId: reportTarget.value?.id,
targetType: reportTarget.value?.type,
description: reportDescription.value,
type: reportReasons.value
})
// 举报成功后关闭对话框并清除信息
closeReportDialog()
return {
success: true,
message: '举报已提交'
}
} catch(error) {
return {
success: false,
message: '举报提交失败,请稍后再试'
}
}
}
5,全部返回
return {
submitReport,
closeReportDialog,
openReportDialog,
reportDescription,
reportTarget,
isReportDialogVisible,
reportReasons
}
父组件中
1,引入reportStore,创造调用并解构
- 引入storeToRefs
- 解构得到举报物名字,类型,举报原因,显示控制,彻底实现数据传递
- 推荐把方法也解构出来,我这里没实现
- 解构后按照正常变量使用就行(记得script中还是要用.value)
import { useReportStore } from '@/stores/reportStore'
import { storeToRefs } from 'pinia'
const reportStore = useReportStore()
const { isReportDialogVisible, reportDescription, reportTarget, reportReasons } = storeToRefs(reportStore)
const getTargetName = computed(() => reportTarget.value.title || `ID ${reportTarget.value.id}` || '未知内容' )
2,提交函数的实现
- 因为store中已实现了提交函数,关闭函数的逻辑,父组件的提交函数仅需实现善后工作,对用户的操作进行反馈
- 拿取store中提交函数返回的消息进行反馈
- 作者这里使用ElMessage和ElLoading实现用户反馈,需要提前引入ElementUI组件库
const onSubmit = async () => {
const { success, message } = await reportStore.submitReport()
if (success){
const loading = ElLoading.service({
lock: true,
text: '正在提交举报中...',
background: 'rgba(0,0,0,0.7)',
})
setTimeout(() => {
loading.close()
ElMessage({
message: `收藏成功`,
type: 'success',
plain: true,
showClose: true
})
ElMessage.success(message)
}, 500)
}else{
ElMessage.warning(`${message}`)
}
}