Next.js集成Sanity时Patch请求耗时超2秒,求排查方案
我搞不懂为什么简单的Sanity Patch请求服务器响应要花2秒多。场景是卡片右滑时触发saveLike和saveVote两个操作,都从Card.js获取卡片信息和用户ID来存储。用Next.js集成Sanity,分别用yarn dev和yarn start运行项目。另外,长期来看,能指导我调试这类查询耗时问题的思路也很重要。
上下文代码
handleRightSwipe函数
const handleRightSwipe = async (cardData, votesData) => { try { await fetch('/api/saveLike', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ likedCards: cardData, currentUser: "abc123", }), }) // 递减投票数 let votesAmount = votesData-- try { await fetch('/api/saveVote', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ _id: "abc123", votes: votesAmount }), }) } catch (error) { console.error(error) } // 更新客户端投票数 setVotesData(votesAmount) // 请求最新投票数 requestVoteAmount(userID) } catch (error) { console.error(error) } }
saveVote API路由
import { client } from '../../lib/sanity' const saveVote = async (req, res) => { try { await client .patch(req.body._id).set({votes:req.body.votes}).commit() res.status(200).send({ message: 'success' }) } catch (error) { res.status(500).send({ message: 'error', data: error.message }) } } export default saveVote
saveLike API路由
import { client } from '../../lib/sanity' const saveLike = async (req, res) => { try { await client .patch(req.body.likedCards._id) .setIfMissing({'likes': []}) .insert('after', 'likes[-1]',[ { _ref: req.body.currentUser, _type: "reference", }, ]) .commit({ autoGenerateArrayKeys: true, }) res.status(200).send({ message: 'success' }) } catch (error) { res.status(500).send({ message: 'error', data: error.message }) } }
Card组件代码
import React, { useState, useMemo, useContext, useRef, useEffect } from 'react' import { Context} from '../context/Context' import TCard from 'react-tinder-card' const Card = () => { const { cardsData, votesData } = useContext(Context) const { handleRightSwipe, handleLeftSwipe, currentAccountAddress } = useContext(Context) const [currentIndex, setCurrentIndex] = useState(cardsData.length - 1) const currentIndexRef = useRef(currentIndex) const childRefs = useMemo( () => Array(cardsData.length) .fill(0) .map((i) => React.createRef()), [cardsData.length] ) const updateCurrentIndex = (val) => { setCurrentIndex(val) currentIndexRef.current = val } const canVote = votesData > 0 const swiped = (direction, card, index) => { if (!canVote) return if (votesData > 0) { if (direction === 'right') { const voterAmount = votesData-- updateCurrentIndex(index - 1) handleRightSwipe(card, currentAccountAddress, voterAmount) } if (direction === 'left') { const voterAmount = votesData-- updateCurrentIndex(index - 1) handleLeftSwipe(card, currentAccountAddress, voterAmount) } } } const outOfFrame = (idx) => { currentIndexRef.current >= idx && childRefs[idx].current.restoreCard() } return ( <div> <div> {cardsData.map((card, index) => ( <TCard card={card} key={card.name} ref={childRefs[index]} preventSwipe={['up', 'down']} onCardLeftScreen={() => outOfFrame(card.name, index)} onSwipe={(dir) => swiped(dir, card, index)}> <div style={{ backgroundImage: `url('${card.imageUrl}')` }}> </div> </TCard> ))} </div> </div> ) } export default Card
即时优化建议
- 并行请求替代串行请求
当前saveLike和saveVote是串行执行,总耗时为两个请求时间之和。改成并行可直接减少总耗时,同时修正原代码中votesData--的逻辑错误(后置递减会导致传递旧值):
const handleRightSwipe = async (cardData, votesData) => { try { const votesAmount = votesData - 1 // 并行发起两个请求 await Promise.all([ fetch('/api/saveLike', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ likedCards: cardData, currentUser: "abc123" }) }), fetch('/api/saveVote', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ _id: "abc123", votes: votesAmount }) }) ]) setVotesData(votesAmount) // 若无多客户端并发修改场景,可去掉requestVoteAmount减少额外请求 // requestVoteAmount(userID) } catch (error) { console.error(error) } }
优化Sanity客户端配置
检查lib/sanity.js中的客户端配置,确保apiVersion为最新稳定版,生产环境开启useCdn: true(读写操作不受CDN影响,但可优化客户端初始化路径),同时选择与服务器地理位置接近的Sanity区域,减少网络延迟。移除不必要的客户端刷新
若没有多客户端同时修改投票数的场景,requestVoteAmount(userID)可直接用本地更新的votesAmount替代,避免额外网络请求耗时。
长期调试耗时问题的思路
拆分测量各阶段耗时
在客户端和API路由中添加计时,精准定位耗时环节:// 客户端测量请求耗时 console.time('saveLike-request') await fetch('/api/saveLike', ...) console.timeEnd('saveLike-request') // API路由中测量Sanity操作耗时 const saveVote = async (req, res) => { try { console.time('sanity-patch-vote') await client.patch(req.body._id).set({votes:req.body.votes}).commit() console.timeEnd('sanity-patch-vote') res.status(200).send({ message: 'success' }) } catch (error) { res.status(500).send({ message: 'error', data: error.message }) } }Sanity Studio监控
进入Sanity Studio的Dataset > Activity面板,查看Patch操作的执行时间,确认是Sanity服务端还是网络环节导致的延迟。对比开发/生产模式差异
yarn dev模式下Next.js有热重载等额外开销,对比yarn start生产模式的耗时,判断是否为开发环境特有问题。网络层排查
用浏览器开发者工具的Network面板,查看请求的DNS解析、TCP连接、等待响应等各阶段耗时,针对性优化网络链路。Sanity数据结构优化
对于likes数组插入操作,可考虑调整数据结构:比如将用户的喜欢列表存储在用户文档中(而非卡片文档),根据查询频率选择更高效的存储方式,减少Patch操作的锁竞争或数据量。
内容的提问来源于stack exchange,提问作者askmeaquestion1234

