如何在同一页面运行两个不同Angular版本应用并实现组件复用
同页面运行Angular 9与Angular 15应用并实现跨应用组件加载
一、基础准备
- 分别构建两个应用,指定不同输出目录避免文件冲突:
- Angular 9:执行
ng build --prod --output-path dist/legacy-app - Angular 15:执行
ng build --output-path dist/new-app
- Angular 9:执行
- 将两个构建后的
dist目录部署到同一Web服务器,确保资源访问路径正确。
二、在同一页面嵌入两个应用
- 在宿主HTML页面中添加独立的DOM容器:
<!-- 旧应用容器 --> <div id="legacy-app-root"></div> <!-- 新应用容器 --> <div id="new-app-root"></div>
- 按顺序引入两个应用的构建产物脚本和样式(Angular 9可能包含
main-es5.js和main-es2015.js,需根据浏览器兼容情况选择):
<!-- 旧应用资源 --> <link rel="stylesheet" href="legacy-app/styles.css"> <script src="legacy-app/runtime-es2015.js"></script> <script src="legacy-app/polyfills-es2015.js"></script> <script src="legacy-app/main-es2015.js"></script> <!-- 新应用资源 --> <link rel="stylesheet" href="new-app/styles.css"> <script src="new-app/runtime.js"></script> <script src="new-app/polyfills.js"></script> <script src="new-app/main.js"></script>
- 修改两个应用的启动逻辑,指定各自的DOM容器:
- Angular 9的
main.ts:
- Angular 9的
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: 'noop' }) .then(moduleRef => { const appRoot = document.getElementById('legacy-app-root'); if (appRoot) { moduleRef.instance.bootstrap(appRoot); } }) .catch(err => console.error(err));
- Angular 15的
main.ts(独立组件模式):
import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { provideZoneChangeDetection } from '@angular/core'; bootstrapApplication(AppComponent, { providers: [provideZoneChangeDetection({ eventCoalescing: true })], ngZone: 'noop' }).then(appRef => { const appRoot = document.getElementById('new-app-root'); if (appRoot) { const rootNode = appRef.injector.get(AppComponent).hostView.rootNodes[0]; appRoot.appendChild(rootNode); } }).catch(err => console.error(err));
三、实现新应用加载旧应用组件
1. 旧应用配置组件导出
- 在旧应用的
angular.json中开启UMD模块打包:
"projects": { "legacy-app": { "architect": { "build": { "options": { "umdModuleIds": { "@angular/core": "ng.core", "@angular/common": "ng.common", "rxjs": "rxjs" }, "allowedCommonJsDependencies": ["rxjs"] } } } } }
- 创建
public-api.ts作为组件导出入口:
export { LegacyComponent } from './app/legacy-component/legacy-component.component'; export { LegacyModule } from './app/legacy-module/legacy-module.module';
2. 新应用引入并加载旧组件
- 在新应用的
angular.json中添加旧应用UMD脚本引用:
"scripts": [ "../dist/legacy-app/main.umd.js" ]
- 在新应用组件中动态加载旧应用组件:
import { Component, ViewChild, ViewContainerRef, OnInit } from '@angular/core'; // 声明旧应用导出的全局变量 declare const legacyApp: { LegacyComponent: any; }; @Component({ selector: 'app-new-host', template: '<div #legacyHost></div>' }) export class NewHostComponent implements OnInit { @ViewChild('legacyHost', { read: ViewContainerRef }) legacyHost!: ViewContainerRef; ngOnInit(): void { // 动态创建旧组件实例 const componentRef = this.legacyHost.createComponent(legacyApp.LegacyComponent); // 传递输入属性 componentRef.instance.legacyInput = '来自新应用的参数'; // 监听旧组件输出事件 componentRef.instance.legacyOutput.subscribe((data: any) => { console.log('旧组件返回数据:', data); }); } }
四、关键注意事项
- Zone.js冲突处理:两个应用默认都会加载Zone.js,会导致变更检测异常,建议在启动时配置
ngZone: 'noop',手动调用componentRef.changeDetectorRef.detectChanges()管理组件变更。 - 依赖版本隔离:Angular 9和15核心库版本差异极大,不要尝试共享依赖,让两个应用各自打包独立依赖包,避免版本冲突。
- 样式隔离:给两个应用的样式添加独特前缀,或在组件中使用
ViewEncapsulation.ShadowDom,防止样式相互污染。 - 内存管理:动态加载旧组件后,在不需要时调用
componentRef.destroy()销毁实例,避免内存泄漏。
内容的提问来源于stack exchange,提问作者user261740
相关产品推荐
相关产品推荐

