TanStack DB Vue 어댑터
설치
npm install @tanstack/vue-db
Vue 컴포저블
Vue Adapter에서 사용할 수 있는 전체 컴포저블 목록은 Vue Functions Reference를 참조할 수 있습니다.
쿼리 작성(필터링, 조인, 집계 등)에 관한 종합적인 문서는 Live Queries Guide를 참조할 수 있습니다.
기본 사용법
useLiveQuery
useLiveQuery 컴포저블은 데이터가 변경될 때 컴포넌트를 자동으로 업데이트하는 라이브 쿼리를 생성합니다. 다음과 같은 반응형 computed ref를 반환합니다.
<script setup>
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'
const { data, isLoading } = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
</script>
<template>
<div v-if="isLoading">Loading...</div>
<ul v-else>
<li v-for="todo in data" :key="todo.id">{{ todo.text }}</li>
</ul>
</template>
참고: 모든 반환 값(data, isLoading, status 등)은 computed ref이므로 .value를 사용해 <script>에서 접근하지만, <template>에서는 직접 접근합니다.
useLiveInfiniteQuery
라이브 업데이트가 지원되는 정렬 및 페이지 매김 데이터에는 useLiveInfiniteQuery를 사용합니다.
<script setup>
import { ref } from 'vue'
import { useLiveInfiniteQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'
const category = ref('news')
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useLiveInfiniteQuery(
(q) =>
q
.from({ posts: postsCollection })
.where(({ posts }) => eq(posts.category, category.value))
.orderBy(({ posts }) => posts.createdAt, 'desc'),
{ pageSize: 20 },
[category],
)
</script>
<template>
<article v-for="post in data" :key="post.id">
{{ post.title }}
</article>
<button
v-if="hasNextPage"
:disabled="isFetchingNextPage"
@click="fetchNextPage()"
>
Load more
</button>
</template>
fetchNextPage()는 페이지 요청이 완료된 후 resolve되는 프로미스를 반환합니다. 실패는 반환된 error ref를 통해 노출되며 프로미스를 reject하지 않습니다.
쿼리에는 orderBy가 포함되어야 합니다. 종속성 배열은 쿼리 함수 형식에서만 사용할 수 있습니다. 정렬된 미리 생성된 라이브 쿼리 컬렉션을 직접 전달할 수도 있습니다.
종속성 배열
useLiveQuery 컴포저블은 마지막 매개변수로 선택적 종속성 배열을 받습니다. 배열의 반응형 값이 변경되면 쿼리가 다시 생성되고 재실행됩니다.
종속성 배열을 사용하는 경우
쿼리가 외부 반응형 값(ref, props 또는 반응형 객체)에 의존하는 경우 종속성 배열을 사용합니다.
<script setup>
import { ref } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { gt } from '@tanstack/db'
const minPriority = ref(5)
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority.value)),
[minPriority] // Pass the ref directly, it will be unwrapped automatically
)
</script>
<template>
<div>{{ data.length }} high-priority todos</div>
</template>
중요: 종속성 배열에는 ref를 함수가 아닌 직접 전달합니다. Vue가 자동으로 추적합니다.
종속성이 변경되면 발생하는 일
종속성 값이 변경되면:
- 이전 라이브 쿼리 컬렉션이 정리됩니다
- 업데이트된 값으로 새 쿼리가 생성됩니다
- 컴포넌트가 새 데이터로 다시 렌더링됩니다
- 컴포저블이 로딩 상태를 다시 표시합니다
모범 사례
쿼리에서 사용하는 모든 외부 ref를 포함합니다:
<script setup>
import { ref } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq, and } from '@tanstack/db'
const userId = ref(1)
const status = ref('active')
// Good - all refs in deps array
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => and(
eq(todos.userId, userId.value),
eq(todos.status, status.value)
)),
[userId, status] // Pass refs directly
)
// Bad - missing dependencies
const { data: badData } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userId.value)),
[] // Missing userId!
)
</script>
<template>
<div>{{ data.length }} todos</div>
</template>
props와 함께 사용:
<script setup>
import { toRef } from 'vue'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'
const props = defineProps<{ userId: number }>()
// Option 1: Convert prop to ref
const userIdRef = toRef(props, 'userId')
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, userIdRef.value)),
[userIdRef]
)
// Option 2: Use a getter function for the prop
const { data: data2 } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.userId, props.userId)),
[() => props.userId] // Getter function for non-ref values
)
</script>
<template>
<div>{{ data.length }} todos</div>
</template>
정적 쿼리에는 빈 배열:
<script setup>
import { useLiveQuery } from '@tanstack/vue-db'
// No external dependencies - query never changes
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection }),
[]
)
</script>
<template>
<div>{{ data.length }} todos</div>
</template>
외부 종속성이 없는 쿼리에는 배열 생략:
<script setup>
import { useLiveQuery } from '@tanstack/vue-db'
// Same as above - no deps needed
const { data } = useLiveQuery(
(q) => q.from({ todos: todosCollection })
)
</script>
<template>
<div>{{ data.length }} todos</div>
</template>
미리 생성된 컬렉션 사용
기존 컬렉션을 useLiveQuery에 전달할 수도 있습니다. 이는 컴포넌트 간에 쿼리를 공유할 때 유용합니다:
<script setup>
import { ref } from 'vue'
import { createLiveQueryCollection } from '@tanstack/db'
import { useLiveQuery } from '@tanstack/vue-db'
import { eq } from '@tanstack/db'
// Create collection outside component or in a composable
const todosQuery = createLiveQueryCollection({
query: (q) => q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.active, true)),
startSync: true
})
// Use the pre-created collection
const { data, collection } = useLiveQuery(todosQuery)
// Or use a reactive ref to switch between collections
const currentQuery = ref(todosQuery)
const { data: reactiveData } = useLiveQuery(currentQuery)
</script>
<template>
<div>{{ data.length }} todos</div>
</template>