专栏名称: 前端早读课
我们关注前端,产品体验设计,更关注前端同行的成长。 每天清晨五点早读,四万+同行相伴成长。
目录
相关文章推荐
51好读  ›  专栏  ›  前端早读课

【第3512期】一个用于简单实时更新的酷炫 HTTP 特性

前端早读课  · 公众号  · 前端  · 2025-05-20 08:00

主要观点总结

本文介绍了作者如何使用HTTP特性实现简单的实时更新技术,特别是在公交实时追踪的网页应用中的实践。作者探索了不同的更新策略,如轮询、服务器发送事件(SSE)以及使用If-Modified-Since和Last-Modified头优化轮询。文章还涵盖了工程优化,如封装Hook提高复用性、服务端判断逻辑抽象为工具函数、容错与错误日志处理等。最后,作者提到了在DigitalOcean App Platform和Cloudflare CDN中遇到的挑战,并描述了如何通过自定义请求头绕过这些挑战。此外,文章还讨论了寻找合适平台的考量因素。

关键观点总结

关键观点1: 实时更新技术的探索和实践


关键观点2: 使用HTTP头优化轮询


关键观点3: 工程优化和封装


关键观点4: 在DigitalOcean App Platform和Cloudflare CDN中的挑战和解决方案


关键观点5: 寻找合适平台的考量因素




正文

请到「今天看啥」查看全文


,
} ,
} ) ;
}

最初的问题其实是 React 的重新 渲染 。我只想在数据真的更新了时才更新 UI。虽然可以在前端判断数据是否有变化,但需要做一次深比较,还是有点麻烦。而公交系统给我的数据里本来就带了一个更新时间戳,所以我就把这个时间戳一起存入 Redis,然后前端拿这个时间戳来判断是否更新。

function VehiclePositions(){
    const [vehicles, setVehicles]= useState<VehiclePosition[]>([]);
    const lastUpdatedRef useRef(0);// Start at 0 so the first one will always be newer

    useEffect(()=>{
        const interval setInterval(async()=>{
            const resp awaitfetch("/api/vehicle-positions");
            if(!resp.okreturn;

            const{ lastUpdated, vehiclePositions await resp.json();
            // If the update time is older than or the same as the previous
            //   Do nothing
            if(lastUpdated <= lastUpdatedRef.currentreturn;
            setVehicles(vehiclePositions);
        },1_000);// Poll every second

        return()=>clearInterval(interval);
    },[]);

    return(
        <>
            {vehicles.map(({ vehicleId, position })=>(
                <Marker
                   key={vehicleId}
                   position={position}
                />
            ))}
        </>
    );
}

这个方案效果已经很好了。但我还是觉得可以更进一步。虽然请求很轻,但大多数情况下我还是在传输和解析一些不会用到的数据。于是我想:既然客户端有更新时间戳,能不能把它发给服务器,服务器只有在数据更新时才返回内容?

最开始我想把时间戳放在 URL 的查询参数里,始终返回 200 状态码,如果响应体为空我就不更新。但总觉得这种做法不太优雅。于是我把想法讲给 ChatGPT,问有没有更标准的做法。我不想让这变成一篇夸 ChatGPT 的文章,但确实在你不知道要查什么关键词的时候,LLM 是很好的工具。ChatGPT 给我指出了 If-Modified-Since 和 Last-Modified 这两个标准 HTTP 头,我查了文档后发现:这不正是我需要的吗!

原理和我之前的 方案 类似,但这是浏览器已经实现好的标准方式。现在我可以把更新时间戳放在响应头里的 Last-Modified ,而浏览器会自动在下次请求时带上 If-Modified-Since 。如果数据没更新,服务器返回一个 304 Not Modified 状态码,表示不需要更新内容。

这个方式比我自定义的方案好太多了。浏览器全自动处理,数据与元数据分离更清晰。而且它的效果跟 SSE 有点像:服务器决定什么时候更新,只在数据变化时才返回内容。而因为时间戳来自公交系统,不是我生成的,也避免了时钟偏差的问题。

整合起来后,我把 “是否更新” 的逻辑从客户端挪到了服务器端,代码还变得更简单了。不仅避免了不必要的渲染,还省下了带宽。我还可以用同一个时间戳来提前结束后台更新任务。虽然车辆位置数据更新本来也不重,但到站时间的数据更复杂,这种优化能节省更多资源。

前端代码:
function VehiclePositions(){
        const[vehicles, setVehicles]= useState<VehiclePosition[]>([]);

        useEffect(()=>{
            const interval setInterval(async()=>{
                const resp awaitfetch("/api/vehicle-positions");
                if(!resp.ok)return;
                if






请到「今天看啥」查看全文