Vue Pinia 实际项目中的典型应用场景与案例分享
2023-07-206.5k 阅读
Vue Pinia 简介
在深入探讨 Vue Pinia 在实际项目中的应用场景与案例之前,我们先来简要回顾一下 Pinia 是什么。Pinia 是 Vue.js 的新一代状态管理库,它基于 Vue 3 的 Composition API 构建,为 Vue 应用提供了一种简单、直观且高效的状态管理方式。与 Vuex 相比,Pinia 有着更简洁的 API 设计,同时保留了诸如状态共享、数据持久化等重要特性。
Pinia 的核心概念包括 Store(存储),它是包含状态、getter 和 action 的容器。状态(state)类似于组件中的 data,用于存储应用的数据;getter 类似于计算属性,用于从状态派生新的数据;action 类似于组件中的方法,用于包含业务逻辑,它可以同步或异步地修改状态。
典型应用场景
多页面状态共享
在一个大型的单页应用(SPA)中,往往存在多个页面需要共享某些状态。例如,一个电商应用中,用户的登录状态、购物车信息等需要在不同页面之间保持一致。
- 代码示例:
首先,创建一个 Pinia Store 来管理用户登录状态。
在登录页面组件中,可以这样使用:import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ isLoggedIn: false, userInfo: null }), getters: { isAuthenticated: (state) => state.isLoggedIn && state.userInfo!== null }, actions: { login(user) { this.isLoggedIn = true; this.userInfo = user; }, logout() { this.isLoggedIn = false; this.userInfo = null; } } });
在需要验证用户登录状态的其他页面组件中:<template> <div> <button @click="handleLogin">登录</button> </div> </template> <script setup> import { useUserStore } from '@/stores/user'; const userStore = useUserStore(); const handleLogin = () => { const user = { name: 'John Doe', email: 'johndoe@example.com' }; userStore.login(user); }; </script>
通过这种方式,不同页面组件可以方便地共享和修改用户登录状态,确保整个应用状态的一致性。<template> <div> <div v-if="userStore.isAuthenticated">欢迎,{{ userStore.userInfo.name }}</div> <div v-else>请先登录</div> </div> </template> <script setup> import { useUserStore } from '@/stores/user'; const userStore = useUserStore(); </script>
复杂业务逻辑封装
当应用中有复杂的业务逻辑,且这些逻辑涉及多个组件之间的交互时,Pinia 可以将这些逻辑封装在 Store 中,提高代码的可维护性和复用性。
- 代码示例:
假设我们有一个任务管理应用,任务之间存在父子关系,并且有任务的排序、筛选等复杂逻辑。
在任务列表组件中,可以使用这些逻辑:import { defineStore } from 'pinia'; export const useTaskStore = defineStore('task', { state: () => ({ tasks: [ { id: 1, title: '任务1', parentId: null }, { id: 2, title: '任务2', parentId: 1 }, { id: 3, title: '任务3', parentId: null } ] }), getters: { getTaskById: (state) => (id) => state.tasks.find(task => task.id === id), getChildTasks: (state) => (parentId) => state.tasks.filter(task => task.parentId === parentId) }, actions: { addTask(task) { this.tasks.push(task); }, sortTasks() { this.tasks.sort((a, b) => a.title.localeCompare(b.title)); }, filterTasksByTitle(title) { return this.tasks.filter(task => task.title.includes(title)); } } });
这样,复杂的任务管理逻辑被封装在 Store 中,各个组件可以方便地调用,使得组件代码更加简洁,业务逻辑也更加集中。<template> <div> <input v-model="filterTitle" placeholder="筛选任务"> <ul> <li v-for="task in filteredTasks" :key="task.id">{{ task.title }}</li> </ul> <button @click="sortTasks">排序任务</button> </div> </template> <script setup> import { useTaskStore } from '@/stores/task'; const taskStore = useTaskStore(); const filterTitle = ref(''); const filteredTasks = computed(() => taskStore.filterTasksByTitle(filterTitle.value)); const sortTasks = () => taskStore.sortTasks(); </script>
数据持久化
在一些应用中,需要将部分状态数据持久化到本地存储(如 localStorage 或 sessionStorage),以便在页面刷新或重新打开应用时,能够恢复之前的状态。
- 代码示例:
以购物车为例,我们希望购物车数据在页面刷新后依然存在。
在购物车组件中:import { defineStore } from 'pinia'; export const useCartStore = defineStore('cart', { state: () => ({ items: [] }), getters: { totalPrice: (state) => state.items.reduce((total, item) => total + item.price * item.quantity, 0) }, actions: { addToCart(item) { const existingItem = this.items.find(i => i.id === item.id); if (existingItem) { existingItem.quantity++; } else { this.items.push({...item, quantity: 1 }); } this.saveToLocalStorage(); }, removeFromCart(id) { this.items = this.items.filter(item => item.id!== id); this.saveToLocalStorage(); }, loadFromLocalStorage() { const data = localStorage.getItem('cart'); if (data) { this.items = JSON.parse(data); } }, saveToLocalStorage() { localStorage.setItem('cart', JSON.stringify(this.items)); } } });
通过在 Store 中添加数据持久化的逻辑,购物车数据可以在页面刷新或关闭后依然保持。<template> <div> <button @click="addToCart({ id: 1, title: '商品1', price: 10 }">添加到购物车</button> <div>总价: {{ cartStore.totalPrice }}</div> </div> </template> <script setup> import { useCartStore } from '@/stores/cart'; const cartStore = useCartStore(); cartStore.loadFromLocalStorage(); const addToCart = (item) => cartStore.addToCart(item); </script>
案例分享
在线教育平台
- 项目背景 我们开发了一个在线教育平台,学生可以在平台上注册、登录,浏览课程,将感兴趣的课程添加到收藏夹,购买课程等。平台有多个页面,如首页、课程列表页、课程详情页、用户中心等。
- 使用 Pinia 的场景
- 用户状态管理:
- 使用 Pinia Store 来管理用户的登录状态、用户信息等。这样在不同页面都能方便地判断用户是否登录,以及获取用户的相关信息。
- 代码实现:
在登录组件中:import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ({ isLoggedIn: false, userInfo: null }), getters: { isAuthenticated: (state) => state.isLoggedIn && state.userInfo!== null }, actions: { login(user) { this.isLoggedIn = true; this.userInfo = user; }, logout() { this.isLoggedIn = false; this.userInfo = null; } } });
在需要显示用户信息的组件中:<template> <div> <input v-model="username" placeholder="用户名"> <input v-model="password" placeholder="密码"> <button @click="handleLogin">登录</button> </div> </template> <script setup> import { useUserStore } from '@/stores/user'; import { ref } from 'vue'; const userStore = useUserStore(); const username = ref(''); const password = ref(''); const handleLogin = () => { // 这里模拟登录请求 const user = { name: username.value, email: 'test@example.com' }; userStore.login(user); }; </script>
<template> <div> <div v-if="userStore.isAuthenticated">欢迎,{{ userStore.userInfo.name }}</div> <div v-else><a href="/login">请登录</a></div> </div> </template> <script setup> import { useUserStore } from '@/stores/user'; const userStore = useUserStore(); </script>
- 课程收藏管理:
- 用户可以在课程列表页和课程详情页将课程添加到收藏夹。收藏夹状态需要在不同页面共享,并且可以进行持久化存储。
- 代码实现:
在课程列表组件中:import { defineStore } from 'pinia'; export const useFavoriteStore = defineStore('favorite', { state: () => ({ courses: [] }), getters: { getFavoriteCourses: (state) => state.courses }, actions: { addCourseToFavorite(course) { this.courses.push(course); this.saveToLocalStorage(); }, removeCourseFromFavorite(courseId) { this.courses = this.courses.filter(course => course.id!== courseId); this.saveToLocalStorage(); }, loadFromLocalStorage() { const data = localStorage.getItem('favoriteCourses'); if (data) { this.courses = JSON.parse(data); } }, saveToLocalStorage() { localStorage.setItem('favoriteCourses', JSON.stringify(this.courses)); } } });
在用户中心的收藏夹页面组件中:<template> <div> <ul> <li v-for="course in courses" :key="course.id"> {{ course.title }} <button @click="addToFavorite(course)">收藏</button> </li> </ul> </div> </template> <script setup> import { useFavoriteStore } from '@/stores/favorite'; import { ref } from 'vue'; const favoriteStore = useFavoriteStore(); const courses = ref([ { id: 1, title: 'Vue 基础课程' }, { id: 2, title: 'React 入门课程' } ]); const addToFavorite = (course) => favoriteStore.addCourseToFavorite(course); favoriteStore.loadFromLocalStorage(); </script>
<template> <div> <h2>我的收藏课程</h2> <ul> <li v-for="course in favoriteStore.getFavoriteCourses" :key="course.id">{{ course.title }}</li> </ul> </div> </template> <script setup> import { useFavoriteStore } from '@/stores/favorite'; const favoriteStore = useFavoriteStore(); favoriteStore.loadFromLocalStorage(); </script>
- 购物车管理:
- 与前面购物车的案例类似,用户在课程详情页可以将课程添加到购物车,购物车数据需要在不同页面共享,并且实现数据持久化。
- 代码实现:
在课程详情页组件中:import { defineStore } from 'pinia'; export const useCartStore = defineStore('cart', { state: () => ({ courses: [] }), getters: { totalPrice: (state) => state.courses.reduce((total, course) => total + course.price, 0) }, actions: { addCourseToCart(course) { this.courses.push(course); this.saveToLocalStorage(); }, removeCourseFromCart(courseId) { this.courses = this.courses.filter(course => course.id!== courseId); this.saveToLocalStorage(); }, loadFromLocalStorage() { const data = localStorage.getItem('cartCourses'); if (data) { this.courses = JSON.parse(data); } }, saveToLocalStorage() { localStorage.setItem('cartCourses', JSON.stringify(this.courses)); } } });
在购物车页面组件中:<template> <div> <h2>{{ course.title }}</h2> <p>价格: {{ course.price }}</p> <button @click="addToCart(course)">加入购物车</button> </div> </template> <script setup> import { useCartStore } from '@/stores/cart'; import { ref } from 'vue'; const cartStore = useCartStore(); const course = ref({ id: 1, title: '高级 JavaScript 课程', price: 50 }); const addToCart = (course) => cartStore.addCourseToCart(course); cartStore.loadFromLocalStorage(); </script>
<template> <div> <h2>购物车</h2> <ul> <li v-for="course in cartStore.courses" :key="course.id">{{ course.title }} - {{ course.price }}</li> </ul> <div>总价: {{ cartStore.totalPrice }}</div> </div> </template> <script setup> import { useCartStore } from '@/stores/cart'; const cartStore = useCartStore(); cartStore.loadFromLocalStorage(); </script>
企业内部管理系统
- 项目背景 为某企业开发内部管理系统,包括员工信息管理、任务分配管理、文件共享等功能。不同角色的员工(如经理、普通员工)有不同的权限,系统有多个页面和复杂的业务逻辑。
- 使用 Pinia 的场景
- 权限管理:
- 根据用户角色确定不同的页面访问权限和操作权限。通过 Pinia Store 管理权限状态,使得在各个组件中都能方便地进行权限判断。
- 代码实现:
在页面组件中判断权限:import { defineStore } from 'pinia'; export const useAuthStore = defineStore('auth', { state: () => ({ role: null, permissions: [] }), getters: { hasPermission: (state) => (permission) => state.permissions.includes(permission) }, actions: { setRole(role) { this.role = role; // 根据角色设置权限 if (role ==='manager') { this.permissions = ['view_all_tasks', 'assign_task', 'edit_employee_info']; } else { this.permissions = ['view_own_tasks']; } } } });
<template> <div> <button v-if="authStore.hasPermission('assign_task')" @click="assignTask">分配任务</button> </div> </template> <script setup> import { useAuthStore } from '@/stores/auth'; const authStore = useAuthStore(); const assignTask = () => { // 分配任务逻辑 }; </script>
- 任务管理:
- 任务有不同的状态(待办、进行中、已完成),并且任务之间可能存在依赖关系。通过 Pinia Store 封装任务管理的逻辑,包括任务的创建、更新、删除以及状态变更等。
- 代码实现:
在任务列表组件中:import { defineStore } from 'pinia'; export const useTaskStore = defineStore('task', { state: () => ({ tasks: [] }), getters: { getTasksByStatus: (state) => (status) => state.tasks.filter(task => task.status === status), getTaskDependencies: (state) => (taskId) => { const task = state.tasks.find(t => t.id === taskId); return task? state.tasks.filter(t => task.dependencies.includes(t.id)) : []; } }, actions: { createTask(task) { this.tasks.push(task); }, updateTask(taskId, updatedTask) { const index = this.tasks.findIndex(task => task.id === taskId); if (index!== -1) { this.tasks[index] = {...this.tasks[index],...updatedTask }; } }, deleteTask(taskId) { this.tasks = this.tasks.filter(task => task.id!== taskId); }, changeTaskStatus(taskId, newStatus) { this.updateTask(taskId, { status: newStatus }); } } });
<template> <div> <ul> <li v-for="task in tasks" :key="task.id"> {{ task.title }} - {{ task.status }} <button @click="changeTaskStatus(task.id, 'in_progress')">开始任务</button> </li> </ul> </div> </template> <script setup> import { useTaskStore } from '@/stores/task'; const taskStore = useTaskStore(); const tasks = computed(() => taskStore.getTasksByStatus('to_do')); const changeTaskStatus = (taskId, newStatus) => taskStore.changeTaskStatus(taskId, newStatus); </script>
- 文件共享状态管理:
- 员工可以上传、下载文件,并且需要记录文件的上传者、上传时间等信息。通过 Pinia Store 管理文件共享相关的状态,方便在不同页面展示文件列表和文件详情。
- 代码实现:
在文件上传组件中:import { defineStore } from 'pinia'; export const useFileStore = defineStore('file', { state: () => ({ files: [] }), getters: { getFilesByUser: (state) => (userId) => state.files.filter(file => file.uploaderId === userId) }, actions: { uploadFile(file) { this.files.push(file); }, downloadFile(fileId) { // 模拟下载逻辑 const file = this.files.find(f => f.id === fileId); if (file) { console.log(`下载文件: ${file.name}`); } } } });
在文件列表组件中:<template> <div> <input type="file" @change="handleFileUpload"> </div> </template> <script setup> import { useFileStore } from '@/stores/file'; import { ref } from 'vue'; const fileStore = useFileStore(); const handleFileUpload = (e) => { const file = e.target.files[0]; const fileInfo = { id: Date.now(), name: file.name, uploaderId: 1 /* 假设用户 ID */ }; fileStore.uploadFile(fileInfo); }; </script>
<template> <div> <ul> <li v-for="file in files" :key="file.id">{{ file.name }} - 上传者: {{ file.uploaderId }}</li> </ul> </div> </template> <script setup> import { useFileStore } from '@/stores/file'; const fileStore = useFileStore(); const files = computed(() => fileStore.files); </script>
通过以上在实际项目中的典型应用场景和案例分享,可以看出 Vue Pinia 在状态管理方面的强大功能和灵活性,它能够有效地提高前端应用的开发效率和可维护性,使代码结构更加清晰,逻辑更加集中。无论是小型项目还是大型复杂项目,Pinia 都能为开发者提供良好的状态管理解决方案。