useHook 和 Pinia 虽然都用于状态管理,但它们的设计目的和使用场景有所不同。useHook 是基于 Vue 3 的 Composition API 封装逻辑的工具,而 Pinia 是一个专门的状态管理库,类似于 Vuex,但更轻量且更适合 Vue 3。
1. useHook 和 Pinia 的区别
| 特性 | useHook (Composition API) | Pinia (状态管理库) |
|---|---|---|
| 目的 | 封装可复用的逻辑 | 集中管理全局状态 |
| 适用范围 | 组件内部或跨组件的逻辑复用 | 全局状态管理,跨组件共享状态 |
| 状态存储 | 状态存储在组件或 Hook 内部 | 状态存储在 Pinia 的 Store 中 |
| 响应式 | 使用 ref、reactive 等实现响应式 | 自动响应式,基于 Vue 3 的响应式系统 |
| 依赖 | 依赖于 Vue 3 的 Composition API | 依赖于 Pinia 库 |
| 适用场景 | 逻辑复用、组件内部状态管理 | 全局状态管理、跨组件状态共享 |
2. useHook 的使用场景
useHook 更适合封装一些与组件相关的逻辑,例如:
- 监听窗口大小变化(
useWindowResize)。 - 监听鼠标位置(
useMousePosition)。 - 封装表单逻辑(
useForm)。 - 封装异步请求逻辑(
useFetch)。
这些逻辑通常是局部的,可能只在某些组件中使用,而不需要全局共享。
示例:useFetch Hook
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const loading = ref(true);
const error = ref(null);
fetch(url)
.then((response) => response.json())
.then((json) => {
data.value = json;
})
.catch((err) => {
error.value = err;
})
.finally(() => {
loading.value = false;
});
return {
data,
loading,
error,
};
}
在组件中使用:
<template>
<div>
<p v-if="loading">Loading...</p>
<p v-else-if="error">Error: {{ error.message }}</p>
<p v-else>{{ data }}</p>
</div>
</template>
<script>
import { useFetch } from './useFetch';
export default {
setup() {
const { data, loading, error } = useFetch('https://api.example.com/data');
return {
data,
loading,
error,
};
},
};
</script>
3. Pinia 的使用场景
Pinia 更适合管理全局状态,例如:
- 用户登录状态。
- 购物车数据。
- 应用主题设置。
- 跨组件共享的数据。
Pinia 提供了一个集中式的 Store 来管理这些状态,并且状态是响应式的,可以在任何组件中访问和修改。
示例:Pinia Store
// stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
}),
actions: {
increment() {
this.count++;
},
decrement() {
this.count--;
},
},
});
在组件中使用:
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script>
import { useCounterStore } from './stores/counter';
export default {
setup() {
const counterStore = useCounterStore();
return {
count: counterStore.count,
increment: counterStore.increment,
decrement: counterStore.decrement,
};
},
};
</script>
4. useHook 和 Pinia 的结合
在实际开发中,useHook 和 Pinia 可以结合使用。例如:
- 使用
useHook封装组件内部的逻辑。 - 使用 Pinia 管理全局状态。
示例:结合使用
// useFetch.js (useHook)
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const loading = ref(true);
const error = ref(null);
fetch(url)
.then((response) => response.json())
.then((json) => {
data.value = json;
})
.catch((err) => {
error.value = err;
})
.finally(() => {
loading.value = false;
});
return {
data,
loading,
error,
};
}
// stores/user.js (Pinia Store)
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
user: null,
}),
actions: {
async fetchUser() {
const { data, loading, error } = useFetch('https://api.example.com/user');
if (!error.value) {
this.user = data.value;
}
},
},
});
在组件中使用:
<template>
<div>
<p v-if="loading">Loading user...</p>
<p v-else-if="error">Error: {{ error.message }}</p>
<p v-else>User: {{ user.name }}</p>
</div>
</template>
<script>
import { useUserStore } from './stores/user';
export default {
setup() {
const userStore = useUserStore();
userStore.fetchUser();
return {
user: userStore.user,
loading: userStore.loading,
error: userStore.error,
};
},
};
</script>
5. 总结
useHook:适合封装组件内部的逻辑,实现逻辑复用。- Pinia:适合管理全局状态,实现跨组件状态共享。
- 结合使用:在实际项目中,可以根据需求将
useHook和 Pinia 结合使用,既能复用逻辑,又能管理全局状态。