用法
创建数据仓库
Vibe 的英语单词意思是 “氛围”,选择这个名字我们希望创建一个在多个组件之间共享的数据仓库,在多个组件之间自由传递。
你可以从 'vue-vine'
中导入 defineVibe
函数,使用它来定义一个数据仓库。
TIP
你可以配置 unplugin-auto-import
来自动导入 defineVibe
函数。
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
imports: [
{
'vue-vine': ['defineVibe'],
}
],
}),
],
})
import { defineVibe } from 'vue-vine'
const [useCounterStore, initCounterStore] = defineVibe('counter', () => {
const count = ref(0)
const increment = () => {
count.value++
}
return { count, increment }
})
如你所见,你需要提供给 defineVibe
两个参数:
- 数据仓库的名字。不需要全局唯一,Vibe 会自动处理相关事宜,并且后续使用都是采用返回的工具方法,也不依赖这个名字。
- 数据仓库的工厂函数。这个函数返回一个对象,这个对象就是数据仓库。你需要在这个工厂函数中定义好数据模型,包括各个状态以及修改状态的方法。
注意
工厂函数返回的值不能是 Promise,Vibe 需要一个同步函数确立数据模型。
defineVibe
的返回值是一对方法,第一个是 useVibe
方法,第二个是 initVibe
方法。之所以采用数组形式返回,是为了方便你在取用时可以直接命名,而不用像 { useVibe: useMyVibe, initVibe: initMyVibe }
这样麻烦。
在顶层组件中初始化
Vibe 适用于状态需要在多层相关组件中共享传递数据的场景,因此你应该在最顶层组件中使用 initVibe
方法来初始化数据仓库。
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
方法获取数据仓库对象,你可以自由地解构出你需要的状态值。
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>
`
}