前言
大家好,我是 hyy,一个热爱技术分享的全栈工程师。
作为一个有着丰富经验的商业型 TypeScript 全栈开发者,我深知技术分享的重要性。我的座右铭是"深入浅出,知其然知其所以然",因为我始终相信,只有真正理解了基础,才能在技术的道路上走得更远。
在这篇文章中,我将带你深入浅出地了解 TanStack Router 这个强大的路由工具。无论你是初学者还是有经验的开发者,相信都能从中获得启发。
为什么选择 TanStack Query?
传统数据获取的痛点
-
重复请求处理
- 多个组件请求相同数据导致重复请求
- 数据更新后需要手动管理缓存
- 页面切换后重新获取数据
-
全局状态管理复杂
- Redux/Vuex 等需要手动处理异步状态
- 需要编写大量的 actions/reducers
- 服务端状态和客户端状态混合
-
性能问题
- 未优化的重复请求影响性能
- 大量数据缓存导致内存占用
- 数据更新不及时
业务场景示例
1. 商品列表与详情
// 问题:列表页和详情页都需要商品数据,如何避免重复请求?
export function useProducts() {
return useQuery({
queryKey: ["products"],
queryFn: async () => {
const { data } = await axios.get("/api/products");
return data;
},
staleTime: 1000 * 60 * 5, // 5分钟内不重新请求
});
}
// 详情页复用缓存数据
export function useProduct(id: string) {
return useQuery({
queryKey: ["product", id],
queryFn: async () => {
const { data } = await axios.get(`/api/products/${id}`);
return data;
},
initialData: () => {
// 从列表缓存中获取初始数据
const products = queryClient.getQueryData(["products"]);
return products?.find((product) => product.id === id);
},
});
}
2. 实时购物车
// 问题:购物车数据需要实时更新,如何保持数据同步?
export function useCart() {
return useQuery({
queryKey: ["cart"],
queryFn: async () => {
const { data } = await axios.get("/api/cart");
return data;
},
// 每分钟自动更新一次
refetchInterval: 60000,
});
}
export function useAddToCart() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (productId: string) => {
const { data } = await axios.post("/api/cart/add", { productId });
return data;
},
// 乐观更新
onMutate: async (productId) => {
await queryClient.cancelQueries({ queryKey: ["cart"] });
const previousCart = queryClient.getQueryData(["cart"]);
queryClient.setQueryData(["cart"], (old: Cart) => ({
...old,
items: [...old.items, { productId, quantity: 1 }],
}));
return { previousCart };
},
onError: (err, variables, context) => {
queryClient.setQueryData(["cart"], context.previousCart);
},
});
}
3. 分页搜索
// 问题:如何处理分页搜索并缓存结果?
export function useSearch(keyword: string, page: number) {
return useQuery({
queryKey: ["search", keyword, page],
queryFn: async () => {
const { data } = await axios.get("/api/search", {
params: { keyword, page },
});
return data;
},
// 只有在关键词和页码都存在时才请求
enabled: !!keyword && page > 0,
// 保持之前页的数据
keepPreviousData: true,
});
}
4. 表单提交与验证
// 问题:如何处理表单提交和服务端验证?
export function useSubmitForm() {
return useMutation({
mutationFn: async (formData: FormData) => {
const { data } = await axios.post("/api/submit", formData);
return data;
},
onError: (error) => {
if (error.response?.status === 422) {
// 处理表单验证错误
setFieldErrors(error.response.data.errors);
}
},
onSuccess: () => {
// 提交成功后刷新相关查询
queryClient.invalidateQueries({ queryKey: ["user-data"] });
},
});
}
5. 实时数据同步
// 问题:如何处理WebSocket实时数据更新?
export function useRealtimeData() {
const queryClient = useQueryClient();
useEffect(() => {
const ws = new WebSocket("ws://api.example.com");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// 实时更新查询缓存
queryClient.setQueryData(["realtime-data"], data);
};
return () => ws.close();
}, []);
return useQuery({
queryKey: ["realtime-data"],
queryFn: async () => {
const { data } = await axios.get("/api/initial-data");
return data;
},
});
}
解决的核心问题
-
数据同步
- 自动处理数据过期和重新获取
- 多组件间数据共享和同步
- 乐观更新与回滚
-
性能优化
- 智能请求去重
- 数据预加载
- 选择性更新
-
开发体验
- 声明式数据获取
- TypeScript 类型安全
- 简化状态管理
-
用户体验
- 加载状态智能处理
- 错误边界处理
- 离线数据支持
结语 & 加学习群 & 摸鱼群
感谢你读到这里!我是 hyy:
- 🚀 一个有着丰富经验的商业型 TypeScript 全栈开发者
- 💼 曾在小型外企、大型外包公司、创业公司等多个领域积累经验
- 📝 掘金技术社区的活跃作者
- 🎵 一个热爱电子音乐的 EDM 爱好者
- 🎮 一个热衷于电子游戏的玩家
- 🌟 一个乐于分享和交流的技术人
如果你也对前端开发充满热情,或者想要:
- 一起学习和进步
- 交流面试经验
- 分享职业发展心得
- 讨论金融、音乐、篮球、历史等兴趣爱好
点这个,有10000多名前端小伙伴在等着一起学习哦 --> 摸鱼沸点