零基础教学:Vue3 + Pinia实现组件共享数据(使用相同数据的组件只需向服务器发送一次请求)
简介
在Vue项目中,当两个/多个组件需要用到相同的服务器数据时,如果分别在各自组件内向服务器请求数据,无疑会增加服务器负担。这里可以用Pinia来实现共享数据,从而只需向服务器发送一次请求。
注:必须有一个父组件作为桥梁(两个子组件为相同数据的使用者)
不一定是父组件,只要是层级更高的组件都可以作为桥梁,比如一级组件作为桥梁,其它二级组件作为数据使用者,如图:
可将图中index.vue
视作一级组件,components
文件夹内的HomeCategory.vue
等视作二级组件。文末有结合代码的详细解释。
)
原理图:
组件分布
这里取三个组件,Son1, Son2都为Father的儿子组件。
- Father.vue
- Son1.vue
- Son2.vue
代码实现及解释
1. 安装pinia
- 终端输入:
npm i pinia
- 在根目录下的
main.js
中使用pinia插件
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
2. 通过pinia创建需要共享的方法和数据
- 在
src/stores
下创建.js
文件categoryStore.js
(路径、名字可自定义)。最后return
的categoryList
为需要的服务器返回的数据,getCategory
为向服务器发送请求的方法 。(getCategoryAPI
是二次封装axios
后调用的函数,这里只需要知道它是发送请求的就行)
//categoryStore.js
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { getCategoryAPI } from '@/apis/layout'
export const useCategoryStore = defineStore('category', () => {
// 导航列表的数据管理
// state
const categoryList = ref([])
// action 获取导航数据的方法
const getCategory = async () => {
const res = await getCategoryAPI()
categoryList.value = res.result
}
return {
categoryList,
getCategory
}
})
Father.vue
中:
<script setup>
//引入子组件
import Son1 from './components/Son1.vue'
import Son2 from './components/Son2.vue'
import { onMounted } from 'vue'
// 触发获取导航列表的action
import { useCategoryStore } from '@/stores/categoryStore'
// 调用categoryStore.js中的useCategoryStore创建对象(pinia的基操,这里看不懂可以随便搜一篇pinia的入门教学,很简单)
const categoryStore = useCategoryStore()
//调用getCategory,向服务器发送请求,也是唯一一次发送请求!!!(关键)
onMounted(() => categoryStore.getCategory())
</script>
<template>
<Son1/>
<Son2/>
</template>
Son1.vue
和Son2.vue
代码相同,这里是Son1.vue
的:
// 这两行和上面的Father.vue一样
import { useCategoryStore } from '@/stores/categoryStore'
const categoryStore = useCategoryStore()
//接下来就可以直接使用categoryList了,比如用来v-for遍历。如果Son2.vue中也需要用到这些数据,同样只需要以上两行
<li class="home" v-for="item in categoryList" :key="item.id">{{ item.name }}</li>
- 解释
- 为什么是在
Father.vue
中创建实例并发送请求而不是在两个子组件中?
因为若是在子组件中分别发送请求,就没有达到为服务器"减负"的目的,同样需要发送两次请求。 - 为什么两个子组件中可以直接使用categoryList?
因为在Father.vue中
已经向服务器发送了请求(调用了getCategory
方法),并将服务器返回的数据赋值给了categoryList
。两个子组件之所以调用useCategoryStore()
方法,是为了通过这个方法创建的实例对象categoryStore
来取得categoryList
并使用,并不需要再调用getCategory
向服务器发送请求。
因此总的来说只发送了一次服务器请求,实现了数据复用的同时减轻了服务器压力。
3. 非直接父子关系组件的情况
现在举例解释文章开头所说的只要是路由等级更高的组件都可以作为桥梁
比如我在Layout/index.vue
中使用了上文中Father.vue
中的同样代码,即向服务器请求数据;那么在Home/HomeCategory.vue
中,我也可以像上文中Son1.vue
中那样创建实例对象categoryStore
来取得categoryList
并使用,尽管这两个组件并非直接父子组件关系。代码完全一样:
import { useCategoryStore } from '@/stores/categoryStore'
const categoryStore = useCategoryStore()
<li class="home" v-for="item in categoryList" :key="item.id">{{ item.name }}</li>
我的理解是因为更高层级的组件先加载,加载后就发送了数据请求并将结果保存在categoryList
中,所以后调用的次一级组件创建categoryStore
实例对象时,categoryList
已经可以具备了刚刚请求返回的数据。因此只要是层级更高的组件都可以作为桥梁请求数据,这样所有使用相同数据的次级组件都只需请求一次。