umijs / qiankun

📦 🚀 Blazing fast, simple and complete solution for micro frontends.
https://qiankun.umijs.org
MIT License
15.71k stars 2k forks source link

官网的angular配置子应用报错:Expected to not be in Angular Zone, but it is! #1453

Open wudith opened 3 years ago

wudith commented 3 years ago

What happens?

主应用:angular9 子应用:angular9

参考官网的angular配置子应用: https://qiankun.umijs.org/zh/guide/tutorial#angular-%E5%BE%AE%E5%BA%94%E7%94%A8

在子应用加载时,浏览器console报以下错误:

main-es2015.2595739ad5e506470d3e.js:118 ERROR Error: Expected to not be in Angular Zone, but it is! at Function.e.assertNotInAngularZone (main.0bb70619174a363f3d3c.js:1073) at Object.next (main.0bb70619174a363f3d3c.js:1073)

在使用 single-spa-angular 插件配置不会有上面错误.

最小可复现仓库

主应用:https://github.com/wudith/parentMFA 子应用:https://github.com/wudith/childMFA

复现步骤,错误日志以及相关配置

分别在主应用和子应用工程执行:

浏览器访问:http://127.0.0.1:4202

运行效果:

出错界面: Image text

相关环境信息

gongshun commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

github-actions[bot] commented 3 years ago

Hello @wudith. In order to facilitate location and troubleshooting, we need you to provide a realistic example. Please forking these link codesandbox or clone qiankun examples to your GitHub repository.

你好 @wudith, 为了方便定位和排查问题,我们需要你提供一个重现实例,请提供一个尽可能精简的链接 codesandbox 或直接 clone qiankun examples,并上传到你的 GitHub 仓库。

wudith commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

已补充复现demo,各位大佬帮看看,谢谢

gongshun commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

已补充复现demo,各位大佬帮看看,谢谢

主应用运行起来,打开http://127.0.0.1:4202是空白

wudith commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

已补充复现demo,各位大佬帮看看,谢谢

主应用运行起来,打开http://127.0.0.1:4202是空白

要子应用和主应用同时运行,http://127.0.0.1:4202(主应用) + http://127.0.0.1:4200(子应用

然后在主应用的http://127.0.0.1:4202 console可以看到报错

gongshun commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

已补充复现demo,各位大佬帮看看,谢谢

主应用运行起来,打开http://127.0.0.1:4202是空白

要子应用和主应用同时运行,http://127.0.0.1:4202(主应用) + http://127.0.0.1:4200(子应用)

然后在主应用的http://127.0.0.1:4202 console可以看到报错

主子项目都运行了的,你可以拉下来跑一下看看

wudith commented 3 years ago

需要复现demo,可以看看这个:https://gitee.com/gongshun1/qiankun-demo

已补充复现demo,各位大佬帮看看,谢谢

主应用运行起来,打开http://127.0.0.1:4202是空白

要子应用和主应用同时运行,http://127.0.0.1:4202(主应用) + http://127.0.0.1:4200(子应用) 然后在主应用的http://127.0.0.1:4202 console可以看到报错

主子项目都运行了的,你可以拉下来跑一下看看

我拉下来试了下,是可以正常运行的,子应用页面就一句话:这是微前端的子应用demo。 主应用运行后也能看到这句话

qyjs commented 3 years ago

我也遇到了 刷新页面就会报这个错误: Expected to not be in Angular Zone, but it is!

Ivy1994 commented 2 years ago

请问解决了吗?我也遇到这个问题了。。。我尝试用另一种用 singla-spa-angular 去配置子服务(https://github.com/umijs/qiankun/tree/master/examples/angular9),虽然不会报这个错但是有其他问题

Tinet-zhangmd commented 2 years ago

请问解决了吗?我也遇到了这个问题,改成single-spa会有什么问题?

请问解决了吗?我也遇到这个问题了。。。我尝试用另一种用 singla-spa-angular 去配置子服务(https://github.com/umijs/qiankun/tree/master/examples/angular9),虽然不会报这个错但是有其他问题。

lanmingle commented 2 years ago

可以看一下这个问题: https://github.com/angular/angular/issues/31870

我测试 qiankun 或者京东的 micro-app 都存在路由或 Zone 问题(注意:主应用或子应用都是 ng 12)

解决方式:

  1. 添加空组件(即路由添加 **)
// qiankun
const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
  },
  {
    path: '**',
    component: EmptyComponent,
  }
];

// micro-app
const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
  },
  {
    path: 'app',
    children: [
      {
        path: 'sub-app1',
        component: Subapp1Component,
        children: [
          {
            path: '**',
            component: EmptyComponent,
          },
        ],
      },
      {
        path: 'sub-app2',
        component: Subapp2Component,
        children: [
          {
            path: '**',
            component: EmptyComponent,
          },
        ],
      },
      {
        path: '**',
        component: EmptyComponent,
      },
    ],
  },
  {
    path: '**',
    component: EmptyComponent,
  },
];
  1. 共享 Zone

... .bootstrapModule(AppModule, { ngZone: (window as any).ngZone })

qiankun


import './public-path';

import { enableProdMode, NgModuleRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

// platformBrowserDynamic()
//   .bootstrapModule(AppModule)
//   .catch(err => console.error(err));

let app: void | NgModuleRef<AppModule>;

async function render() {
  app = await platformBrowserDynamic()
    .bootstrapModule(AppModule, { ngZone: (window as any).ngZone })
    .catch((err) => console.error(err));
}

if (!(window as any).__POWERED_BY_QIANKUN__) {
  console.log("sub-app1 -> render");
  render();
}

export async function bootstrap(props: Object) {
  console.log("sub-app1 -> bootstrap:", props);
  // @ts-ignore
}

export async function mount(props: Object) {
  console.log("sub-app1 -> mount:", props);
  // @ts-ignore
  render();
}

export async function unmount(props: Object) {
  console.log("sub-app1 -> unmount:", props);
  // @ts-ignore
  app.destroy();
}

micro-app

import './public-path';
import { enableProdMode, NgModuleRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

// platformBrowserDynamic()
//   .bootstrapModule(AppModule)
//   .catch(err => console.error(err));

let app: any = null;

platformBrowserDynamic()
  .bootstrapModule(AppModule, { ngZone: (window as any).ngZone })
  .then((res: NgModuleRef<AppModule>) => app = res)
  .catch(err => console.error(err));

window.addEventListener('unmount', function () {
  if (app) {
    app.destroy();
    app = null;
  }
});

// let app: any = null;

// async function mount() {
//   app = await platformBrowserDynamic()
//     .bootstrapModule(AppModule)
//     .catch(err => console.error(err));
// }

// function unmount() {
//   if (app) {
//     app.destroy();
//     app = null;
//   }
//   app = null;
// }

// if ((window as any).__MICRO_APP_ENVIRONMENT__) {
//   (window as any)[`micro-app-${(window as any).__MICRO_APP_NAME__}`] = { mount, unmount };
// } else {
//   mount();
// }
  1. 在 AppModule 添加

export class AppModule {

  constructor(private ngZone: NgZone) {
    (window as any).ngZone = this.ngZone;
  }

}

只作参考,这些代码只是测试,应该编写更严谨的逻辑,如:非微应用不执行 (window as any).ngZone = this.ngZone; 这个逻辑等等

sxhieen commented 2 years ago

我也是通过把zone暴露在window下,解决共享zone的问题,想看看有没有更好的解决方法

yyq1988 commented 2 years ago

我这边主应用angular13,微应用angular7,暴漏zone也没用 ,版本13用的import "zone.js", 而版本7用的import 'zone.js/dist/zone'

DavisGYF commented 1 year ago

这个问题解决了么,我也出现了这个问题,我主/子应用都是angular13,然后子应用一个使用single-spa-angula插件改造子应用1,一个按照官网不使用插件,一步一步的改造的子应用2,目前两个项目都能运行成功! 上上面这句报错出现在不使用插件改造的子应用2中,并且在主项目里面,第一次点击子项目调转路由跳过去不报错,然后再刷新就爆粗了, 并且还有其他3个问题, 问题一 子应用1,更改报错以后可以自动更新,子应用2不能自动更新, 问题二 从主应用先跳转到子应用1在直接点击跳转子应用二不会报错,但是如果从主应用先跳转到子应用2,在跳转到子应用1,url里面的activeRouter改变了,但是项目下面也报错 问题三,运行成功以后,直接在浏览器输入子应用1的地址页面空白,但是子应用2的页面可以正常显示,并且子应用2没有报错,

DavisGYF commented 1 year ago

过把zone暴露在window下,解决共享zone的问题, 这个方法我试了,我这儿还有问题

wudith commented 1 year ago

这个问题解决了么,我也出现了这个问题,我主/子应用都是angular13,然后子应用一个使用single-spa-angula插件改造子应用1,一个按照官网不使用插件,一步一步的改造的子应用2,目前两个项目都能运行成功! 上上面这句报错出现在不使用插件改造的子应用2中,并且在主项目里面,第一次点击子项目调转路由跳过去不报错,然后再刷新就爆粗了, 并且还有其他3个问题, 问题一 子应用1,更改报错以后可以自动更新,子应用2不能自动更新, 问题二 从主应用先跳转到子应用1在直接点击跳转子应用二不会报错,但是如果从主应用先跳转到子应用2,在跳转到子应用1,url里面的activeRouter改变了,但是项目下面也报错 问题三,运行成功以后,直接在浏览器输入子应用1的地址页面空白,但是子应用2的页面可以正常显示,并且子应用2没有报错,

没有解决,现在项目都统一用singla-spa-angular 去配置子应用来规避了这个报错

mlxgzzz commented 9 months ago

我这边主应用angular13,微应用angular7,暴漏zone也没用 ,版本13用的import "zone.js", 而版本7用的import 'zone.js/dist/zone'

hello 你最后有解决吗?

Levi-Montgomery commented 4 months ago

Same problem here. Except using module federations, both main and sub apps are angular 17