Angular 8迁移至15:Ivy下Runtime动态组件创建方案咨询
Angular 15 + Ivy 下实现Runtime动态创建组件(服务器加载未知模板)
可以实现这类需求,Angular 15 虽然默认使用AOT编译器并废弃了旧的Compiler、ComponentFactoryResolver API,但仍支持JIT编译来处理动态模板场景,结合Ivy引擎的新能力可以完成你的需求。
核心实现思路
- 保留动态创建组件类、模块类的逻辑,移除
entryComponents(Ivy已自动处理组件入口) - 使用
@angular/platform-browser-dynamic提供的compileModuleAndAllComponentsAsync替代废弃的Compiler.compileModuleAndAllComponentsAsync - 通过
ViewContainerRef.createComponent完成组件实例化与挂载
完整实现示例
import { Component, NgModule, ViewContainerRef, Injector, Type, ComponentRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IonicModule } from '@ionic/angular'; import { TagsxModule } from './path/to/your/tagsx.module'; // 替换为你的实际路径 import { NO_ERRORS_SCHEMA } from '@angular/core'; import { compileModuleAndAllComponentsAsync } from '@angular/platform-browser-dynamic'; // 假设你的WordReferenceDto定义 interface WordReferenceDto { // 你的DTO字段 } export class DynamicCardService { constructor( private injector: Injector, private viewContainerRef: ViewContainerRef ) {} // 动态创建组件类 private createDynamicComponent(selector: string, template: string): Type<any> { @Component({ selector: selector, template: template }) class CustomComponent { parent: any = {}; wordRef: WordReferenceDto; clicked: any; added: any; play: any; delegate(action: string, arg: any): void { const func = this.parent[action]; func?.call(this.parent, arg); } } return CustomComponent; } // 动态创建模块类 private createDynamicModule(component: Type<any>): Type<any> { @NgModule({ declarations: [component], imports: [ CommonModule, IonicModule, TagsxModule ], schemas: [NO_ERRORS_SCHEMA] // 允许模板中出现未声明的元素/属性 }) class CustomModule {} return CustomModule; } // 渲染动态组件的入口方法 async renderDynamicProductCard(template: string, selector: string, parentContext: any): Promise<ComponentRef<any>> { // 1. 创建组件与模块类 const customComponent = this.createDynamicComponent(selector, template); const customModule = this.createDynamicModule(customComponent); // 2. JIT编译模块与组件 const moduleCompileResult = await compileModuleAndAllComponentsAsync(customModule); const componentFactory = moduleCompileResult.componentFactories.find(f => f.componentType === customComponent); if (!componentFactory) { throw new Error('Failed to compile dynamic component'); } // 3. 创建并挂载组件实例 const componentRef = this.viewContainerRef.createComponent( componentFactory, undefined, this.injector ); // 4. 传递父上下文(用于事件委托) componentRef.instance.parent = parentContext; return componentRef; } }
关键配置与注意事项
- 启用JIT编译:在
angular.json的项目build配置中关闭AOT(因为动态模板必须JIT编译):
"projects": { "your-app-name": { "architect": { "build": { "options": { "aot": false, // 其他原有配置... } } } } }
- 性能优化:由于你的模板数量高达20万,建议对编译后的
ComponentFactory进行缓存,以避免重复编译相同模板:
// 在服务中添加缓存对象 private factoryCache = new Map<string, any>(); // 在render方法中先检查缓存 async renderDynamicProductCard(template: string, selector: string, parentContext: any): Promise<ComponentRef<any>> { const cacheKey = template + selector; if (this.factoryCache.has(cacheKey)) { const componentFactory = this.factoryCache.get(cacheKey); const componentRef = this.viewContainerRef.createComponent(componentFactory, undefined, this.injector); componentRef.instance.parent = parentContext; return componentRef; } // 原有编译逻辑... // 编译完成后存入缓存 this.factoryCache.set(cacheKey, componentFactory); }
安全防护:服务器返回的HTML模板必须经过XSS过滤,避免恶意脚本注入。虽然Angular会自动转义模板中的内容,但仍需对输入的模板进行严格校验。
Ivy兼容性:上述代码完全基于Angular 15的Ivy引擎实现,无需额外配置兼容选项。
内容的提问来源于stack exchange,提问作者k31
相关产品推荐
相关产品推荐

