vue3 useHook 和 Pinia 区别

570 阅读2分钟

useHook 和 Pinia 虽然都用于状态管理,但它们的设计目的和使用场景有所不同。useHook 是基于 Vue 3 的 Composition API 封装逻辑的工具,而 Pinia 是一个专门的状态管理库,类似于 Vuex,但更轻量且更适合 Vue 3。

1. useHook 和 Pinia 的区别

特性useHook (Composition API)Pinia (状态管理库)
目的封装可复用的逻辑集中管理全局状态
适用范围组件内部或跨组件的逻辑复用全局状态管理,跨组件共享状态
状态存储状态存储在组件或 Hook 内部状态存储在 Pinia 的 Store 中
响应式使用 refreactive 等实现响应式自动响应式,基于 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 结合使用,既能复用逻辑,又能管理全局状态。