Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions