要优化的原因

如上图,普通导航与吸顶导航中的列表是完全一致的,但是要发送两次网络请求,存在浪费。
解决: 通过 Pinia 集中状态管理数据,再把数据分发给组件使用。
复习一下两个组件请求的大致步骤
- 在utils/http.js 文件里进行 axios 实例 httpInstance 的创建,进行一些默认配置,如 baseURL 基地址、timeout 请求超出时间。
- 在apis/Layout.js 引入实例httpInstance,封装获取分类的函数 getCategoryAPI(),在函数内部使用实例发送请求,并导出函数将其暴露出去。
- 在目标组件中,导入封装好的函数,再单独编写一个获取数据的业务函数 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中的数据
- 实现了请求去重和数据共享的目标