Skip to content

用法

创建数据仓库

Vibe 的英语单词意思是 “氛围”,选择这个名字我们希望创建一个在多个组件之间共享的数据仓库,在多个组件之间自由传递。

你可以从 'vue-vine' 中导入 defineVibe 函数,使用它来定义一个数据仓库。

TIP

你可以配置 unplugin-auto-import 来自动导入 defineVibe 函数。

ts
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'

export default defineConfig({
  plugins: [
    AutoImport({
      imports: [
        {
          'vue-vine': ['defineVibe'],
        }
      ],
    }),
  ],
})
vue-vine
import { defineVibe } from 'vue-vine'

const [useCounterStore, initCounterStore] = defineVibe('counter', () => {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  return { count, increment }
})

如你所见,你需要提供给 defineVibe 两个参数:

  1. 数据仓库的名字。不需要全局唯一,Vibe 会自动处理相关事宜,并且后续使用都是采用返回的工具方法,也不依赖这个名字。
  2. 数据仓库的工厂函数。这个函数返回一个对象,这个对象就是数据仓库。你需要在这个工厂函数中定义好数据模型,包括各个状态以及修改状态的方法。

注意

工厂函数返回的值不能是 Promise,Vibe 需要一个同步函数确立数据模型。

defineVibe 的返回值是一对方法,第一个是 useVibe 方法,第二个是 initVibe 方法。之所以采用数组形式返回,是为了方便你在取用时可以直接命名,而不用像 { useVibe: useMyVibe, initVibe: initMyVibe } 这样麻烦。

在顶层组件中初始化

Vibe 适用于状态需要在多层相关组件中共享传递数据的场景,因此你应该在最顶层组件中使用 initVibe 方法来初始化数据仓库。

vue-vine
const [useProductStore, initProductStore] = defineVibe('products', () => {
  const productList = ref([])
  const fetchProducts = async () => {
    const resp = await fetch('...')
    productList.value = resp.data
  }
  return { productList, fetchProducts }
})

function App() {
  initProductStore(async () => {
    await fetchProducts()
  })

  return vine`...`
}

初始化数据仓库时,你可以不传参数直接调用,也可以传入一个函数作为初始化执行器,这个函数会接收数据仓库对象作为参数。

调用 initProductStore 这样的 Vibe 初始化方法,是为了使用 Vue 的 provide API 将数据提供给下层组件。

我们强力推荐你在创建数据仓库模型时对状态都使用 ref 来定义,这样你就可以方便地在初始化执行器中从参数上解构出它们,而不会像 Pinia 那样丢失响应性。

需要注意的细节

初始化执行器函数可以是异步的,Vue 和 Vine 在机制上并不会等待其执行完毕。

因此你应该对执行器中可能会更新的状态值保持警惕,在组件中使用时应该为其设置兜底方案,比如设置一个加载动画等视觉方案。

如果你坚持要在组件 setup 逻辑中等待这个 initVibe 方法执行完毕,你可以在前面加上 await,但这也意味着你的组件 setup 函数会变成异步的,Vue 要求你后续必须用 Suspense 组件包裹你的组件。

在下层组件中使用

使用 useVibe 方法获取数据仓库对象,你可以自由地解构出你需要的状态值。

vue-vine
function ProductList() {
  const { productList } = useProductStore()

  return vine`
    <div v-if="productList.length" class="product-list">
      <div v-for="product in productList" :key="product.id">
        <ProductCard :product="product" />
      </div>
    </div>
    <div v-else>
      <p>Loading ...</p>
    </div>
  `
}