ES Modules静态不可拦截import,但可用import()动态加载配合Proxy包装导出对象实现运行时控制。1. 基础方式:await import()后Proxy拦截get/has等;2. 缓存懒加载:Map缓存Proxy,首次访问触发加载;3. 沙箱安全:过滤危险属性、重写fetch等;4. 注意事项:无法拦截静态import,模块顶层代码仍会执行。

ES Modules 本身是静态的,不能直接用 Proxy 动态拦截 import(因为 import 语句在编译时解析,不可运行时重写)。但你可以用 import()(动态导入)配合 Proxy,在运行时控制模块加载行为、包装导出、做缓存、权限检查等。核心思路是:用 Proxy 包裹动态导入返回的模块对象,而非模块本身。
1. 基础动态导入 + Proxy 包装导出对象
这是最常用也最实用的方式:先 import() 加载模块,再用 Proxy 拦截其导出属性访问。
- 确保目标模块是 ESM(含
type="module"或 .mjs 后缀) -
import()返回 Promise,需 await;Proxy 作用于 resolved 后的模块命名空间对象 - 可拦截
get、has、ownKeys等,实现日志、校验、默认值兜底等
async function createModuleProxy(modulePath) {
const mod = await import(modulePath);
return new Proxy(mod, {
get(target, prop) {
console.log(`[Proxy] 访问模块 ${modulePath} 的 ${String(prop)}`);
if (prop in target) return target[prop];
throw new Error(`模块未导出 ${String(prop)}`);
},
has(target, prop) {
console.log(`[Proxy] 检查 ${String(prop)} 是否存在`);
return prop in target;
}
});
}
// 使用
const api = await createModuleProxy('./api.mjs');
console.log(api.fetchUser()); // 触发 get 拦截
2. 实现模块缓存与懒加载代理
避免重复 import,同时延迟加载——适合按需加载大型模块或插件系统。
- 用 WeakMap 或 Map 缓存已加载模块的 Proxy 实例
- 首次访问时才执行 import(),后续复用 Proxy 对象
- Proxy 内部可结合 loading 状态、错误重试逻辑
const moduleCache = new Map();
function lazyModule(modulePath) {
if (moduleCache.has(modulePath)) {
return moduleCache.get(modulePath);
}
const proxy = new Proxy(
{},
{
async get(_, prop) {
if (prop === '__load__') {
const mod = await import(modulePath);
moduleCache.set(modulePath, mod);
return mod;
}
const mod = await import(modulePath);
moduleCache.set(modulePath, mod);
return mod[prop];
}
}
);
moduleCache.set(modulePath, proxy);
return proxy;
}
// 第一次访问触发加载
const utils = lazyModule('./utils.mjs');
console.log(await utils.formatDate(new Date())); // 自动加载并执行
3. 模块沙箱与安全代理(限制/重写导出)
适用于插件系统或用户脚本场景,防止恶意访问全局、篡改原生方法或暴露敏感 API。
- 用 Proxy 过滤掉不希望暴露的导出名(如
process、require) - 重写特定函数,注入上下文、打点、或 mock 返回值
- 可组合
Reflect.get+ 条件判断,实现细粒度控制
async function sandboxedImport(path) {
const mod = await import(path);
const blocked = new Set(['process', 'globalThis', 'eval', 'require']);
return new Proxy(mod, {
get(target, prop) {
if (blocked.has(prop)) {
throw new Error(`禁止访问 ${String(prop)}`);
}
if (prop === 'fetch') {
return (...args) => {
console.warn('[Sandbox] fetch 被拦截,返回 mock 数据');
return Promise.resolve({ ok: true, json: () => ({ mocked: true }) });
};
}
return target[prop];
}
});
}
4. 注意事项与边界限制
Proxy 无法改变 import 本身的静态特性,以下情况不支持:
- 不能拦截
import { x } from './a.mjs'这类静态 import 语句 - 不能让
import()返回非模块对象(比如 Proxy 包裹一个空对象再塞进模块逻辑 —— 不合法) - 模块顶层代码仍会在首次
import()时执行,Proxy 只能干预导出对象的访问行为 - TypeScript 中需配
"module": "esnext"和"target": "es2020"以上,且类型推导对 Proxy 导出可能不完整,建议用declare module补充类型











