如何在Angular组件中按需加载不兼容SSR的NgApexchartsModule
解决Angular SSR中组件层面加载NgApexchartsModule的方案
完全可以在组件层面实现仅浏览器环境下加载NgApexchartsModule,以下是两种可行的落地方案:
方案1:封装动态加载组件
通过创建一个包装组件,在组件内部判断浏览器环境后,动态导入模块和图表组件,避免SSR阶段加载不兼容代码。
1.1 编写包装组件
import { Component, OnInit, ViewChild, ViewContainerRef, Inject, Injector } from '@angular/core'; import { PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; @Component({ selector: 'app-apex-chart-wrapper', template: `<ng-template #chartHost></ng-template>` }) export class ApexChartWrapperComponent implements OnInit { @ViewChild('chartHost', { read: ViewContainerRef }) chartHost!: ViewContainerRef; constructor( @Inject(PLATFORM_ID) private platformId: Object, private injector: Injector ) {} async ngOnInit() { if (isPlatformBrowser(this.platformId)) { // 动态导入NgApexcharts模块和你的图表业务组件 const { NgApexchartsModule } = await import('ng-apexcharts'); const { SalesChartComponent } = await import('./sales-chart/sales-chart.component'); // 创建模块引用并生成组件实例 const moduleRef = await NgApexchartsModule.create(this.injector); const componentFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(SalesChartComponent); const componentRef = this.chartHost.createComponent(componentFactory); // 传递组件输入属性(按需添加) componentRef.instance.series = this.seriesData; componentRef.instance.chartConfig = this.chartOptions; } } // 定义需要传递给图表组件的数据 seriesData = [{ name: '销售额', data: [31, 40, 28, 51, 42, 109, 100] }]; chartOptions = { xaxis: { categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'] } }; }
1.2 替换原组件引用
把页面中直接使用<apx-chart>的地方,替换为<app-apex-chart-wrapper>,服务器端渲染时只会渲染空模板,不会触发图表加载。
1.3 声明包装组件
将ApexChartWrapperComponent添加到根模块或共享模块的declarations数组中,无需在模块中导入NgApexchartsModule。
方案2:*ngIf结合浏览器环境判断(简化版)
如果不需要复杂的动态模块加载,可以直接在业务组件中通过isPlatformBrowser判断,仅在浏览器环境下渲染图表组件。
2.1 调整业务组件代码
import { Component, Inject } from '@angular/core'; import { PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; @Component({ selector: 'app-dashboard', template: ` <div class="dashboard-content"> <!-- 仅浏览器环境渲染图表 --> <apx-chart *ngIf="isBrowser" [series]="series" [chart]="chartConfig" [xaxis]="xaxis"> </apx-chart> </div> ` }) export class DashboardComponent { isBrowser = isPlatformBrowser(this.platformId); // 图表配置 series = [{ name: '访问量', data: [100, 200, 150, 300, 250] }]; chartConfig = { type: 'bar' }; xaxis = { categories: ['周一', '周二', '周三', '周四', '周五'] }; constructor(@Inject(PLATFORM_ID) private platformId: Object) {} }
2.2 模块层面的浏览器专属导入
在Angular CLI生成的SSR项目结构中,会有app.browser.module.ts文件,仅在浏览器环境加载,将NgApexchartsModule导入到这个文件中:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; import { NgApexchartsModule } from 'ng-apexcharts'; @NgModule({ imports: [ BrowserModule.withServerTransition({ appId: 'serverApp' }), AppModule, NgApexchartsModule // 仅浏览器环境导入 ], bootstrap: [AppComponent] }) export class AppBrowserModule {}
关键注意事项
- 动态加载时确保
import()路径正确,避免编译阶段报错。 - 如果需要传递组件输出事件,可通过
componentRef.instance.eventName.subscribe()绑定。 - 两种方案都能确保SSR阶段不会执行浏览器专属代码,彻底规避ng-apexcharts的兼容性问题。
内容的提问来源于stack exchange,提问作者Felipe Scherer
相关产品推荐
相关产品推荐

