TanStack 功能介绍与使用场景TanStack是一个专注于构建无头Headless、类型安全且框架无关的工具库集合。它的前身是著名的React Query团队现已发展成为一个支持React, Vue, Solid, Svelte, Angular等多前端框架的生态系统。其核心理念是“学会一次到处复用”。你只需要掌握一套 API 逻辑就可以在任何支持的框架中使用极大地降低了跨框架开发的学习成本和迁移成本。核心功能模块与使用场景模块原名核心功能典型使用场景TanStack QueryReact Query异步状态管理。自动处理数据获取、缓存、后台同步、加载/错误状态、重试机制、窗口聚焦重新获取等。替代手写的axiosuseStateuseEffect组合管理复杂的服务器状态需要离线缓存或乐观更新的场景。TanStack TableReact Table无头表格逻辑。提供排序、过滤、分页、分组、虚拟滚动等核心逻辑不渲染任何 HTML完全由你控制 UI。构建高度定制化的企业级数据表格需要复杂交互如多列排序、服务端分页的后台管理系统。TanStack Router-类型安全的路由。利用文件系统路由和强大的类型推导实现参数、搜索查询的端到端类型安全。对类型安全要求极高的中大型应用需要预加载数据或权限控制的复杂路由场景。TanStack VirtualReact Virtual列表虚拟化。高效渲染超长列表或网格只渲染可视区域内的 DOM 元素。聊天历史记录、无限滚动的信息流、大型数据表格的行虚拟化。TanStack Form-表单管理。提供高性能的表单状态管理支持复杂的验证逻辑常与 Zod 结合。复杂的动态表单、向导式表单、需要高性能验证的大型表单。TanStack Start-全栈元框架。基于 Vite 和 TanStack Router提供 SSR、流式传输、服务器函数等能力。构建类似 Next.js/Nuxt 的全栈应用但希望使用 TanStack 生态的统一体验。完整使用示例Vue 3 vs React以下将以最常用的TanStack Query(数据获取) 和TanStack Table(表格展示) 为例对比 Vue 3 和 React 的完整用法。你会发现逻辑几乎一模一样只是语法糖不同。1. TanStack Query: 数据获取与缓存场景从 API 获取用户列表并支持删除用户突变。️ Vue 3 示例 (tanstack/vue-query)安装:npminstalltanstack/vue-query1. 初始化 (main.ts):import{createApp}fromvueimport{VueQueryPlugin}fromtanstack/vue-queryimportAppfrom./App.vueconstappcreateApp(App)app.use(VueQueryPlugin)// 注册插件app.mount(#app)2. 使用组件 (UserList.vue):script setup langts import { useQuery, useMutation, useQueryClient } from tanstack/vue-query import axios from axios // --- 1. 查询数据 (GET) --- const fetchUsers async () { const { data } await axios.get(https://jsonplaceholder.typicode.com/users) return data } const { data, isLoading, error, refetch } useQuery({ queryKey: [users], // 唯一的缓存键 queryFn: fetchUsers, staleTime: 1000 * 60 * 5, // 5分钟内数据视为新鲜不重新请求 }) // --- 2. 修改数据 (DELETE) --- const queryClient useQueryClient() const deleteMutation useMutation({ mutationFn: (id: number) axios.delete(https://jsonplaceholder.typicode.com/users/${id}), onSuccess: () { // 成功后使 users 查询失效触发自动重新获取 queryClient.invalidateQueries({ queryKey: [users] }) }, }) const handleDelete (id: number) { if(confirm(确定删除)) { deleteMutation.mutate(id) } } /script template div h2用户列表 (Vue 3)/h2 !-- 加载状态 -- div v-ifisLoading加载中.../div !-- 错误状态 -- div v-else-iferror错误: {{ (error as Error).message }}/div !-- 数据列表 -- ul v-else li v-foruser in data :keyuser.id {{ user.name }} button clickhandleDelete(user.id) :disableddeleteMutation.isPending.value {{ deleteMutation.isPending.value ? 删除中... : 删除 }} /button /li /ul button clickrefetch stylemargin-top: 20px;手动刷新/button /div /template⚛️ React 示例 (tanstack/react-query)安装:npminstalltanstack/react-query1. 初始化 (main.tsx):import React from react import ReactDOM from react-dom/client import { QueryClient, QueryClientProvider } from tanstack/react-query import App from ./App const queryClient new QueryClient() // 创建客户端实例 ReactDOM.createRoot(document.getElementById(root)!).render( QueryClientProvider client{queryClient} {/* 包裹应用 */} App / /QueryClientProvider, )2. 使用组件 (UserList.tsx):import { useQuery, useMutation, useQueryClient } from tanstack/react-query import axios from axios export default function UserList() { const queryClient useQueryClient() // --- 1. 查询数据 (GET) --- const { data, isLoading, error, refetch } useQuery({ queryKey: [users], queryFn: async () { const { data } await axios.get(https://jsonplaceholder.typicode.com/users) return data }, staleTime: 1000 * 60 * 5, }) // --- 2. 修改数据 (DELETE) --- const deleteMutation useMutation({ mutationFn: (id: number) axios.delete(https://jsonplaceholder.typicode.com/users/${id}), onSuccess: () { queryClient.invalidateQueries({ queryKey: [users] }) }, }) if (isLoading) return div加载中.../div if (error) return div错误: {(error as Error).message}/div return ( div h2用户列表 (React)/h2 ul {data?.map((user: any) ( li key{user.id} {user.name}{ } button onClick{() handleDelete(user.id)} disabled{deleteMutation.isPending} {deleteMutation.isPending ? 删除中... : 删除} /button /li ))} /ul button onClick{() refetch()} style{{ marginTop: 20px }}手动刷新/button /div ) function handleDelete(id: number) { if (confirm(确定删除)) { deleteMutation.mutate(id) } } }对比总结: 逻辑配置queryKey,queryFn,invalidateQueries完全一致。Vue 中使用.value访问响应式对象如isPending.value而 React 直接访问属性。2. TanStack Table: 高级表格场景展示用户数据支持排序和分页。TanStack Table 是 “Headless” 的意味着它只提供逻辑行、列、页码计算你需要自己写table标签。️ Vue 3 示例 (tanstack/vue-table)安装:npminstalltanstack/vue-table组件代码 (UserTable.vue):script setup langts import { ref, computed } from vue import { FlexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useVueTable, } from tanstack/vue-table // 模拟数据 const data ref([ { id: 1, name: Alice, age: 25 }, { id: 2, name: Bob, age: 30 }, { id: 3, name: Charlie, age: 35 }, { id: 4, name: David, age: 20 }, ]) // 定义列 const columns [ { header: 姓名, accessorKey: name, cell: ({ getValue }: any) getValuestring(), }, { header: 年龄, accessorKey: age, cell: ({ getValue }: any) getValuenumber(), }, ] // 初始化表格 const table useVueTable({ data, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), state: { // 可以在此绑定响应式状态 }, }) /script template div table border1 cellpadding8 thead tr v-forheaderGroup in table.getHeaderGroups() :keyheaderGroup.id th v-forheader in headerGroup.headers :keyheader.id clickheader.column.toggleSorting() stylecursor: pointer; user-select: none; FlexRender :renderheader.column.columnDef.header :propsheader.getContext() / {{ header.column.getIsSorted() ? (header.column.getIsSorted() asc ? : ) : }} /th /tr /thead tbody tr v-forrow in table.getRowModel().rows :keyrow.id td v-forcell in row.getVisibleCells() :keycell.id FlexRender :rendercell.column.columnDef.cell :propscell.getContext() / /td /tr /tbody /table !-- 分页控件 -- div stylemargin-top: 10px; button clicktable.setPageIndex(0) :disabled!table.getCanPreviousPage() 首页 /button button clicktable.previousPage() :disabled!table.getCanPreviousPage() 上一页 /button span 第 {{ table.getState().pagination.pageIndex 1 }} 页 /span button clicktable.nextPage() :disabled!table.getCanNextPage() 下一页 /button button clicktable.setPageIndex(table.getPageCount() - 1) :disabled!table.getCanNextPage() 末页 /button /div /div /template⚛️ React 示例 (tanstack/react-table)安装:npminstalltanstack/react-table组件代码 (UserTable.tsx):import { useState } from react import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, ColumnDef, } from tanstack/react-table type Person { id: number; name: string; age: number } const data: Person[] [ { id: 1, name: Alice, age: 25 }, { id: 2, name: Bob, age: 30 }, { id: 3, name: Charlie, age: 35 }, { id: 4, name: David, age: 20 }, ] const columns: ColumnDefPerson[] [ { header: 姓名, accessorKey: name, cell: ({ getValue }) getValuestring(), }, { header: 年龄, accessorKey: age, cell: ({ getValue }) getValuenumber(), }, ] export default function UserTable() { const [sorting, setSorting] useState([]) const [pagination, setPagination] useState({ pageIndex: 0, pageSize: 2 }) const table useReactTable({ data, columns, state: { sorting, pagination }, onSortingChange: setSorting, onPaginationChange: setPagination, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), }) return ( div table border1 cellPadding8 thead {table.getHeaderGroups().map((headerGroup) ( tr key{headerGroup.id} {headerGroup.headers.map((header) ( th key{header.id} onClick{header.column.getToggleSortingHandler()} style{{ cursor: pointer, userSelect: none }} {flexRender(header.column.columnDef.header, header.getContext())} {{ asc: , desc: }[header.column.getIsSorted() as string] ?? null} /th ))} /tr ))} /thead tbody {table.getRowModel().rows.map((row) ( tr key{row.id} {row.getVisibleCells().map((cell) ( td key{cell.id} {flexRender(cell.column.columnDef.cell, cell.getContext())} /td ))} /tr ))} /tbody /table {/* 分页控件 */} div style{{ marginTop: 10px }} button onClick{() table.setPageIndex(0)} disabled{!table.getCanPreviousPage()} 首页 /button button onClick{() table.previousPage()} disabled{!table.getCanPreviousPage()} 上一页 /button span 第 {table.getState().pagination.pageIndex 1} 页 /span button onClick{() table.nextPage()} disabled{!table.getCanNextPage()} 下一页 /button button onClick{() table.setPageIndex(table.getPageCount() - 1)} disabled{!table.getCanNextPage()} 末页 /button /div /div ) }对比总结:总结TanStack 的最大优势在于统一性。一旦你理解了queryKey的概念或useTable的配置项你就可以在 React、Vue 甚至 Solid 项目中无缝切换无需重新学习新的 API。对于现代前端工程化而言它是提升开发效率和代码质量的神器。
TanStack功能介绍和使用场景,对应 vue,react 完整使用示例
发布时间:2026/6/10 23:09:47
TanStack 功能介绍与使用场景TanStack是一个专注于构建无头Headless、类型安全且框架无关的工具库集合。它的前身是著名的React Query团队现已发展成为一个支持React, Vue, Solid, Svelte, Angular等多前端框架的生态系统。其核心理念是“学会一次到处复用”。你只需要掌握一套 API 逻辑就可以在任何支持的框架中使用极大地降低了跨框架开发的学习成本和迁移成本。核心功能模块与使用场景模块原名核心功能典型使用场景TanStack QueryReact Query异步状态管理。自动处理数据获取、缓存、后台同步、加载/错误状态、重试机制、窗口聚焦重新获取等。替代手写的axiosuseStateuseEffect组合管理复杂的服务器状态需要离线缓存或乐观更新的场景。TanStack TableReact Table无头表格逻辑。提供排序、过滤、分页、分组、虚拟滚动等核心逻辑不渲染任何 HTML完全由你控制 UI。构建高度定制化的企业级数据表格需要复杂交互如多列排序、服务端分页的后台管理系统。TanStack Router-类型安全的路由。利用文件系统路由和强大的类型推导实现参数、搜索查询的端到端类型安全。对类型安全要求极高的中大型应用需要预加载数据或权限控制的复杂路由场景。TanStack VirtualReact Virtual列表虚拟化。高效渲染超长列表或网格只渲染可视区域内的 DOM 元素。聊天历史记录、无限滚动的信息流、大型数据表格的行虚拟化。TanStack Form-表单管理。提供高性能的表单状态管理支持复杂的验证逻辑常与 Zod 结合。复杂的动态表单、向导式表单、需要高性能验证的大型表单。TanStack Start-全栈元框架。基于 Vite 和 TanStack Router提供 SSR、流式传输、服务器函数等能力。构建类似 Next.js/Nuxt 的全栈应用但希望使用 TanStack 生态的统一体验。完整使用示例Vue 3 vs React以下将以最常用的TanStack Query(数据获取) 和TanStack Table(表格展示) 为例对比 Vue 3 和 React 的完整用法。你会发现逻辑几乎一模一样只是语法糖不同。1. TanStack Query: 数据获取与缓存场景从 API 获取用户列表并支持删除用户突变。️ Vue 3 示例 (tanstack/vue-query)安装:npminstalltanstack/vue-query1. 初始化 (main.ts):import{createApp}fromvueimport{VueQueryPlugin}fromtanstack/vue-queryimportAppfrom./App.vueconstappcreateApp(App)app.use(VueQueryPlugin)// 注册插件app.mount(#app)2. 使用组件 (UserList.vue):script setup langts import { useQuery, useMutation, useQueryClient } from tanstack/vue-query import axios from axios // --- 1. 查询数据 (GET) --- const fetchUsers async () { const { data } await axios.get(https://jsonplaceholder.typicode.com/users) return data } const { data, isLoading, error, refetch } useQuery({ queryKey: [users], // 唯一的缓存键 queryFn: fetchUsers, staleTime: 1000 * 60 * 5, // 5分钟内数据视为新鲜不重新请求 }) // --- 2. 修改数据 (DELETE) --- const queryClient useQueryClient() const deleteMutation useMutation({ mutationFn: (id: number) axios.delete(https://jsonplaceholder.typicode.com/users/${id}), onSuccess: () { // 成功后使 users 查询失效触发自动重新获取 queryClient.invalidateQueries({ queryKey: [users] }) }, }) const handleDelete (id: number) { if(confirm(确定删除)) { deleteMutation.mutate(id) } } /script template div h2用户列表 (Vue 3)/h2 !-- 加载状态 -- div v-ifisLoading加载中.../div !-- 错误状态 -- div v-else-iferror错误: {{ (error as Error).message }}/div !-- 数据列表 -- ul v-else li v-foruser in data :keyuser.id {{ user.name }} button clickhandleDelete(user.id) :disableddeleteMutation.isPending.value {{ deleteMutation.isPending.value ? 删除中... : 删除 }} /button /li /ul button clickrefetch stylemargin-top: 20px;手动刷新/button /div /template⚛️ React 示例 (tanstack/react-query)安装:npminstalltanstack/react-query1. 初始化 (main.tsx):import React from react import ReactDOM from react-dom/client import { QueryClient, QueryClientProvider } from tanstack/react-query import App from ./App const queryClient new QueryClient() // 创建客户端实例 ReactDOM.createRoot(document.getElementById(root)!).render( QueryClientProvider client{queryClient} {/* 包裹应用 */} App / /QueryClientProvider, )2. 使用组件 (UserList.tsx):import { useQuery, useMutation, useQueryClient } from tanstack/react-query import axios from axios export default function UserList() { const queryClient useQueryClient() // --- 1. 查询数据 (GET) --- const { data, isLoading, error, refetch } useQuery({ queryKey: [users], queryFn: async () { const { data } await axios.get(https://jsonplaceholder.typicode.com/users) return data }, staleTime: 1000 * 60 * 5, }) // --- 2. 修改数据 (DELETE) --- const deleteMutation useMutation({ mutationFn: (id: number) axios.delete(https://jsonplaceholder.typicode.com/users/${id}), onSuccess: () { queryClient.invalidateQueries({ queryKey: [users] }) }, }) if (isLoading) return div加载中.../div if (error) return div错误: {(error as Error).message}/div return ( div h2用户列表 (React)/h2 ul {data?.map((user: any) ( li key{user.id} {user.name}{ } button onClick{() handleDelete(user.id)} disabled{deleteMutation.isPending} {deleteMutation.isPending ? 删除中... : 删除} /button /li ))} /ul button onClick{() refetch()} style{{ marginTop: 20px }}手动刷新/button /div ) function handleDelete(id: number) { if (confirm(确定删除)) { deleteMutation.mutate(id) } } }对比总结: 逻辑配置queryKey,queryFn,invalidateQueries完全一致。Vue 中使用.value访问响应式对象如isPending.value而 React 直接访问属性。2. TanStack Table: 高级表格场景展示用户数据支持排序和分页。TanStack Table 是 “Headless” 的意味着它只提供逻辑行、列、页码计算你需要自己写table标签。️ Vue 3 示例 (tanstack/vue-table)安装:npminstalltanstack/vue-table组件代码 (UserTable.vue):script setup langts import { ref, computed } from vue import { FlexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useVueTable, } from tanstack/vue-table // 模拟数据 const data ref([ { id: 1, name: Alice, age: 25 }, { id: 2, name: Bob, age: 30 }, { id: 3, name: Charlie, age: 35 }, { id: 4, name: David, age: 20 }, ]) // 定义列 const columns [ { header: 姓名, accessorKey: name, cell: ({ getValue }: any) getValuestring(), }, { header: 年龄, accessorKey: age, cell: ({ getValue }: any) getValuenumber(), }, ] // 初始化表格 const table useVueTable({ data, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), state: { // 可以在此绑定响应式状态 }, }) /script template div table border1 cellpadding8 thead tr v-forheaderGroup in table.getHeaderGroups() :keyheaderGroup.id th v-forheader in headerGroup.headers :keyheader.id clickheader.column.toggleSorting() stylecursor: pointer; user-select: none; FlexRender :renderheader.column.columnDef.header :propsheader.getContext() / {{ header.column.getIsSorted() ? (header.column.getIsSorted() asc ? : ) : }} /th /tr /thead tbody tr v-forrow in table.getRowModel().rows :keyrow.id td v-forcell in row.getVisibleCells() :keycell.id FlexRender :rendercell.column.columnDef.cell :propscell.getContext() / /td /tr /tbody /table !-- 分页控件 -- div stylemargin-top: 10px; button clicktable.setPageIndex(0) :disabled!table.getCanPreviousPage() 首页 /button button clicktable.previousPage() :disabled!table.getCanPreviousPage() 上一页 /button span 第 {{ table.getState().pagination.pageIndex 1 }} 页 /span button clicktable.nextPage() :disabled!table.getCanNextPage() 下一页 /button button clicktable.setPageIndex(table.getPageCount() - 1) :disabled!table.getCanNextPage() 末页 /button /div /div /template⚛️ React 示例 (tanstack/react-table)安装:npminstalltanstack/react-table组件代码 (UserTable.tsx):import { useState } from react import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, ColumnDef, } from tanstack/react-table type Person { id: number; name: string; age: number } const data: Person[] [ { id: 1, name: Alice, age: 25 }, { id: 2, name: Bob, age: 30 }, { id: 3, name: Charlie, age: 35 }, { id: 4, name: David, age: 20 }, ] const columns: ColumnDefPerson[] [ { header: 姓名, accessorKey: name, cell: ({ getValue }) getValuestring(), }, { header: 年龄, accessorKey: age, cell: ({ getValue }) getValuenumber(), }, ] export default function UserTable() { const [sorting, setSorting] useState([]) const [pagination, setPagination] useState({ pageIndex: 0, pageSize: 2 }) const table useReactTable({ data, columns, state: { sorting, pagination }, onSortingChange: setSorting, onPaginationChange: setPagination, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), }) return ( div table border1 cellPadding8 thead {table.getHeaderGroups().map((headerGroup) ( tr key{headerGroup.id} {headerGroup.headers.map((header) ( th key{header.id} onClick{header.column.getToggleSortingHandler()} style{{ cursor: pointer, userSelect: none }} {flexRender(header.column.columnDef.header, header.getContext())} {{ asc: , desc: }[header.column.getIsSorted() as string] ?? null} /th ))} /tr ))} /thead tbody {table.getRowModel().rows.map((row) ( tr key{row.id} {row.getVisibleCells().map((cell) ( td key{cell.id} {flexRender(cell.column.columnDef.cell, cell.getContext())} /td ))} /tr ))} /tbody /table {/* 分页控件 */} div style{{ marginTop: 10px }} button onClick{() table.setPageIndex(0)} disabled{!table.getCanPreviousPage()} 首页 /button button onClick{() table.previousPage()} disabled{!table.getCanPreviousPage()} 上一页 /button span 第 {table.getState().pagination.pageIndex 1} 页 /span button onClick{() table.nextPage()} disabled{!table.getCanNextPage()} 下一页 /button button onClick{() table.setPageIndex(table.getPageCount() - 1)} disabled{!table.getCanNextPage()} 末页 /button /div /div ) }对比总结:总结TanStack 的最大优势在于统一性。一旦你理解了queryKey的概念或useTable的配置项你就可以在 React、Vue 甚至 Solid 项目中无缝切换无需重新学习新的 API。对于现代前端工程化而言它是提升开发效率和代码质量的神器。