React 的 cache 功能是 React 18 中引入的,用于实现自动缓存组件或数据,减少重复计算和网络请求,提升应用性能。它主要在服务器端渲染(Server-Side Rendering, SSR)和并发模式(Concurrent Rendering)下发挥作用,通过缓存计算密集型或异步请求的结果来提高性能和用户体验。
React的缓存函数有助于防止函数在具有相同参数的情况下重复执行,从而节省计算资源并提高应用程序的整体效率。要使用缓存函数,您需要用cache包装目标函数,React会负责存储函数调用的结果。当用相同的参数再次调用包装过的函数时,React首先检查缓存。如果缓存中存在这些参数的结果,它将返回缓存的结果,而不是再次执行函数。
import { cache } from 'react'; import { Suspense } from 'react'; const fetchWeatherData = async (city) => { console.log(`Fetching weather data for ${city}...`); // 堆代码 duidaima.com // 模拟API调用 await new Promise(resolve => setTimeout(resolve, 2000)); return { temperature: Math.round(Math.random() * 30), conditions: ['Sunny', 'Cloudy', 'Rainy'][Math.floor(Math.random() * 3)], }; }; const getCachedWeatherData = cache(fetchWeatherData); async function WeatherWidget({ city }) { const weatherData = await getCachedWeatherData(city); return ( <div> <h2>Weather in {city}</h2> <p>Temperature: {weatherData.temperature}</p> <p>Conditions: {weatherData.conditions}</p> </div> ); } function WeatherDashboard() { return ( <div> <Suspense fallback={<div>Loading New York weather...</div>}> <WeatherWidget city="New York" /> </Suspense> <Suspense fallback={<div>Loading London weather...</div>}> <WeatherWidget city="London" /> </Suspense> <Suspense fallback={<div>Loading New York weather...</div>}> {/* 故意重复 */} <WeatherWidget city="New York" /> </Suspense> <Suspense fallback={<div>Loading Tokyo weather...</div>}> <WeatherWidget city="Tokyo" /> </Suspense> </div> ); } export default WeatherDashboard;
在上述代码中,缓存函数应用于fetchWeatherData,创建了一个新的函数getCachedWeatherData,它记住了天气数据获取的结果。然后,在WeatherWidget组件中使用这个缓存的函数来检索不同城市的天气信息。
WeatherDashboard组件渲染了多个WeatherWidget实例,包括纽约的一个重复实例,这是故意的。这作为缓存机制的关键概念证明,因为它防止了在同一渲染周期内多次请求相同数据时的冗余昂贵操作,通过重用第一次调用的缓存结果,避免了不必要的网络请求。
这种缓存机制有几个优点:它减少了API调用的数量,从而提高了性能并降低了服务器负载;它确保了请求相同信息的组件之间的数据一致性;它简化了组件代码,通过自动处理潜在的重复请求。
import { cache } from 'react'; // 包装函数,用于缓存函数并设置过期时间 const cacheWithTTL = (fn, ttl) => { const cachedResults = new Map(); return cache((...args) => { const key = JSON.stringify(args); const now = Date.now(); if (cachedResults.has(key)) { const { value, timestamp } = cachedResults.get(key); // 判断缓存是否过期 if (now - timestamp < ttl) { return value; // 缓存未过期,返回缓存的值 } } // 执行原始函数,获取结果并存入缓存 const value = fn(...args); cachedResults.set(key, { value, timestamp: now }); return value; }); }; // 使用示例 const fetchData = cacheWithTTL(async (url) => { const response = await fetch(url); return response.json(); }, 5000); // 设置 TTL 为 5 秒 // 调用示例 async function getData() { const data = await fetchData('/api/data'); // 第一次执行并缓存 console.log(data); const cachedData = await fetchData('/api/data'); // 5 秒内再次调用,直接返回缓存 console.log(cachedData); }注意事项
需要注意的是,React的缓存函数仅适用于服务器组件。每次调用缓存都会创建一个新的记忆化函数,这意味着用相同的函数多次调用缓存将导致独立的不共享相同缓存的记忆化版本。另外要注意的是,缓存函数既缓存成功的结果也缓存错误。因此,如果函数因某些参数抛出错误,该错误将被缓存,并在随后使用相同参数的调用中重新抛出。
import { cache } from 'react'; // 包装函数,确保异常不会被缓存 const safeCache = (fn) => { return cache(async (...args) => { try { // 正常执行并返回结果 return await fn(...args); } catch (error) { // 捕获异常,但不缓存结果 console.error('Error caught in cached function:', error); throw error; // 继续抛出异常,调用者可以决定如何处理 } }); }; // 堆代码 duidaima.com // 示例:带有异常处理的 API 请求 const fetchData = safeCache(async (url) => { const response = await fetch(url); if (!response.ok) { throw new Error('Network response was not ok'); // 如果请求失败,抛出异常 } return response.json(); // 返回成功的响应数据 }); // 使用示例 async function getData() { try { const data = await fetchData('/api/data'); console.log('Fetched data:', data); } catch (error) { console.error('Failed to fetch data:', error); // 处理异常情况 } }结论