Pinia 优化重复请求-小兔鲜项目普通导航与吸顶导航请求两次数据

要优化的原因

如上图,普通导航与吸顶导航中的列表是完全一致的,但是要发送两次网络请求,存在浪费。

解决: 通过 Pinia 集中状态管理数据,再把数据分发给组件使用。

复习一下两个组件请求的大致步骤

  1. 在utils/http.js 文件里进行 axios 实例 httpInstance 的创建,进行一些默认配置,如 baseURL 基地址、timeout 请求超出时间。
  2. 在apis/Layout.js 引入实例httpInstance,封装获取分类的函数 getCategoryAPI(),在函数内部使用实例发送请求,并导出函数将其暴露出去。
  3. 在目标组件中,导入封装好的函数,再单独编写一个获取数据的业务函数 getCategory()并进行 async 修饰,在业务函数内部使用变量 res 来接收通过封装好的函数请求到的数据,再用响应式变量接收进行赋值,然后在onMounted钩子函数中调用getCategory()函数,最后在模版中 v-for 渲染categoryList。

核心优化原理

1. 状态提升

  • 将共享数据从组件内部提升到全局Store中
  • 避免多个组件各自维护相同数据

2. 请求去重

  • 通过统一的action触发请求
  • 确保相同数据只请求一次

3. 数据共享

  • 多个组件共享同一份数据源
  • 数据更新时所有组件自动同步

代码实现

步骤一: 新增 stores/category.js 分类列表

javascript 复制代码
<!-- 导入 -->
import { defineStore } from 'pinia'
import { ref } from 'vue' 
import { getCategoryAPI } from '@/apis/layout' 

// 使用defineStore方法
// 参数一为标识,是一个字符串,参数二为回调函数
// 在defineStore方法里 属性就是state 函数就是action
export const useCategoryStore = defineStore('category', () => {
// 导航列表的数据管理  -和之前在组件里的写法一样
// state 导航列表数据 action获取导航数据的方法
const categoryList = ref([])
const getCategory = async () => {
    //逻辑
    const res = await getCategoryAPI()
    categoryList.value = res.result
}

// 要想在组件里面用 需要以对象的方式把数据和函数return出去
return { categoryList, getCategory }
})

为什么不需要在 store 中使用 onMounted?(Store与组件生命周期的关注点分离)

  • Store的职责:
    • 管理应用状态(state)
    • 提供修改状态的方法 (actions)
    • 保持数据的一致性和可预测性
    • 不应该包含组件特定的逻辑
  • 组件的职责:
    • 管理UI渲染
    • 处理用户交互
    • 控制组件生命周期
    • 调用Store的actions来获取或修改数据

步骤二: 在两个导航的父组件 Layout/index.vue 中 触发获取导航列表的 action

javascript 复制代码
// 触发获取导航列表的action -导入 执行 调用内部函数

import { onMounted } from 'vue'

// 导入 use 开头的方法
import { useCategoryStore } from '@/stores/category' 

// 执行 useCategoryStore 方法 得到实例对象 categoryStore
const categoryStore = useCategoryStore()

// 在 onMounted 钩子中 通过这个实例对象调取内部的 action 函数 getCategory()
onMounted(async () => {
await categoryStore.getCategory()
console.log(
        categoryStore.categoryList,
        'Layout组件调用getCategory方法,用async修饰可以确保在数据返回后再进行操作,这样可以输出categoryStore.categoryList,否则会输出array[0]'
        )
})

注意的点:用 async 修饰钩子函数 onMounted 可以确保在数据返回后再进行操作

步骤三: 在两个子组件即普通导航组件和吸顶导航组件里面导入 use 开头的方法,执行得到实例对象,通过实例对象拿到里面的 CategoryList(步骤二使得里面已经获取到数据了),最后在模版中渲染数据即可

xml 复制代码
<script setup>
import { useCategoryStore } from '@/stores/category'
const categoryStore = useCategoryStore()
</script>

<template>
<li v-for="item in categoryStore.categoryList" :key="item.id">
    {{ item.name }}
</li>
</template>

最佳实践建议

1. Store设计原则

  • 按业务领域划分Store
  • 保持Store职责单一
  • 合理组织state和action

2. 数据获取时机

  • 在合适的组件层级触发数据获取
  • 避免在多个组件中重复触发相同请求
  • 考虑数据的缓存和更新策略

结语

  • 仅在父组件 Layout/index.vue 中请求了一次数据
  • head普通导航组件和fixed吸顶导航组件直接使用Store中的数据
  • 实现了请求去重和数据共享的目标
相关推荐
A了LONE9 小时前
uniapp的上拉加载H5和小程序
前端·javascript·vue.js
前端小巷子9 小时前
Vue 路由传参的四种方式
前端·vue.js·面试
YL有搞头14 小时前
VUE的模版渲染过程
前端·javascript·vue.js·面试·模版渲染
某公司摸鱼前端19 小时前
一键 i18n 国际化神库!适配 Vue、React!
前端·vue.js·react.js·i18n
给月亮点灯|20 小时前
Vue基础知识-重要的内置关系:vc实例.__proto__.__proto__ === Vue.prototype
前端·vue.js·原型模式
正义的大古21 小时前
OpenLayers 入门篇教程 -- 章节三 :掌控地图的视野和交互
开发语言·vue.js
定栓1 天前
vue3入门- script setup详解下
前端·vue.js·typescript
定栓1 天前
vue3入门- script setup详解上
前端·javascript·vue.js
jason_yang1 天前
vue3中定义组件的4种姿势
前端·vue.js