JavaScript跨模块导入时memoize缓存是否可共享?
先修正你示例代码的两处笔误,否则所有缓存逻辑都不会生效:
const dataPromise(url) = ...存在语法错误,正确写法是const dataPromise = async (url) => ...- 你在
getData.js里导出的是经memoize包装的memDataPromise,但两个业务模块导入的都是未包装的原始dataPromise,这种写法下哪怕同模块内的重复请求也不会走缓存。
默认缓存跨模块共享规则
修正导入写法(两个模块都导入memDataPromise)的前提下,跨模块请求相同地址默认就可以复用缓存,不需要额外配置。
原因是ES模块遵循单例机制:同一个模块被应用内多个地方导入时,模块内部的代码只会在首次导入时执行一次,后续所有导入拿到的都是同一个导出实例。_.memoize生成的包装函数、以及函数内部挂载的缓存存储都是全局唯一的,不管多少个模块调用这个包装函数,所有请求都会读写同一份缓存。
不仅是完成请求后的结果会被复用,哪怕第一个请求还处于pending状态,其他模块发起的同地址请求也会直接拿到同一个pending中的Promise实例,不会重复发起网络请求。
会出现缓存不共享的场景
如果出现以下情况,会导致缓存隔离:
- 不同模块导入的不是同一个包装后的函数实例,比如在模块内写了工厂函数,每次调用都重新执行
_.memoize生成新的包装函数 - 微前端沙箱、多版本npm包共存等特殊场景,导致
getData.js被重复执行,生成了多份函数实例 - 手动修改了memoize函数的cache属性,替换成了非单例的存储实例
强制跨场景共享缓存的实现方案
如果遇到特殊场景默认单例机制失效,可以手动指定全局唯一的缓存存储,彻底避免隔离问题:
方案1:给memoize指定全局共享缓存
// getData.js import _ from 'lodash'; // 挂载到globalThis实现全局唯一,重复导入时优先读取已存在的缓存 const sharedCache = globalThis.__FETCH_MEM_CACHE__ ?? new Map(); globalThis.__FETCH_MEM_CACHE__ = sharedCache; const dataPromise = async (url) => fetch(url).then(res => res.json()); export const memDataPromise = _.memoize(dataPromise, url => url, { cache: { get: key => sharedCache.get(key), set: (key, val) => sharedCache.set(key, val), has: key => sharedCache.has(key), delete: key => sharedCache.delete(key), clear: () => sharedCache.clear() } });
方案2:自行封装单例缓存层,不依赖lodash memoize的默认存储
单独抽离缓存模块,保证缓存实例全局唯一:
// cache.js 单独维护全局请求缓存 export const globalRequestCache = new Map(); // getData.js import { globalRequestCache } from './cache.js'; const dataPromise = async (url) => { if (globalRequestCache.has(url)) { return globalRequestCache.get(url); } const reqPromise = fetch(url).then(res => res.json()); globalRequestCache.set(url, reqPromise); // 请求失败时删除缓存,避免后续请求直接拿到失败结果 reqPromise.catch(() => globalRequestCache.delete(url)); return reqPromise; }; export const memDataPromise = dataPromise;
这种写法完全可控,也不需要额外引入依赖,缓存逻辑完全透明,适合对缓存规则有自定义需求的场景。
内容的提问来源于stack exchange,提问作者Seldon Stone
相关产品推荐
相关产品推荐

