Skip to content

【页面卡死】管道使用window变量触发defer方法,zone.js监听再次触发变更检测,变更检测再次访问 window,形成死循环 #1686

@paomoshuilingling

Description

@paomoshuilingling

问题描述

问题的具体描述
createProxyWindow 函数中,proxyWindowget 拦截器第一行就是:

get: (target: microAppWindowType, key: PropertyKey): unknown => {
  throttleDeferForSetAppName(appName) // <--- 罪魁祸首
  // ... 后续逻辑
}

无论访问什么属性(即使该属性在 target 上存在,或者是 __MICRO_APP_ 开头的内部变量),都会先执行 throttleDeferForSetAppName。而在底层实现中,这个函数会调用 defer(微任务),导致 Zone.js 监控到异步并触发变更检测,变更检测再次访问 window,形成死循环。

由于这是框架底层 Proxy 的硬编码逻辑,通过常规 options 配置(如 pluginsescapeProperties)是无法拦截这第一行代码的执行的

复现步骤

  1. pipe管道里面去windows上拿变量,然后这个window被微前端框架代理了,里面有个defer的微任务,触发这个后就会导致主应用的全局的那个zone有微任务的队列。
  2. 这里结束不了,继续触发管道,管道逻辑里又去拿windows上的变量,然后就一直循环。

上传截图

请上传代码截图、控制台、终端等截图以帮助我们了解您的问题。
src/libs/utils.ts (或对应编译后的文件) 中的 defer 函数

建议:

以下是三种深度解决方案:

方案一:修改源码(通过 patch-package)—— 最推荐

这是最彻底的办法。你需要修改 micro-app 源码中 defer 的实现,或者在调用处强制使用原生 Promise。

  1. 修改 src/libs/utils.ts (或对应编译后的文件) 中的 defer 函数:
// 将原来的 Promise.resolve().then(fn) 
// 改为使用 rawWindow 上的原生 Promise
export function defer (fn: Function): void {
  const nativePromise = (window as any).rawWindow?.Promise || (window as any).__zone_symbol__Promise || Promise;
  nativePromise.resolve().then(fn);
}

或者直接修改 createProxyWindow

// 在 micro-app 源码的 get 拦截器中
get: (target: microAppWindowType, key: PropertyKey): unknown => {
  // 如果是 Zone.js 相关的 key,直接跳过 throttleDefer
  if (typeof key === 'string' && key.startsWith('__zone_symbol__')) {
    return Reflect.get(rawWindow, key);
  }
  
  throttleDeferForSetAppName(appName);
  // ...
}

复现仓库

请提供一个精简的代码仓库,然后上传到自己的 github,以帮助我们复现您的问题。

环境信息

  • micro-app版本:1.0.0-rc.26
  • 主应用前端框架&版本:angular17
  • 子应用前端框架&版本:angular20
  • 构建工具&版本:webpack

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions