xieziyu / ngx-echarts

An angular (ver >= 2.x) directive for ECharts (ver >= 3.x)
https://xieziyu.github.io/ngx-echarts/
MIT License
1.11k stars 197 forks source link

Tree Shaking not reducing the overall package size of an Angular application #312

Closed msbasanth closed 3 years ago

msbasanth commented 3 years ago

Hi,

We compared using ngx-echarts with full echarts against echarts tree shakable API's. For experimenting we used the demo sample given @https://github.com/xieziyu/ngx-echarts-starter

Option 1: Full ECharts imported

@NgModule({
  imports: [
    NgxEchartsModule.forRoot({
      /**
       * This will import all modules from echarts.
       * If you only need custom modules,
       * please refer to [Custom Build] section.
       */
      echarts: () => import('echarts'), // or import('./path-to-my-custom-echarts')
    }),
  ],
})

Our observation is this: image

Total Main.js Size: 1.1 MB; Total Network transfer: 1.2MB

Option 2: Using tree shakable API's from ECharts

import * as echarts from 'echarts/core';
import { LineChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  GridComponent
} from 'echarts/components';
// Import the Canvas renderer, note that introducing the CanvasRenderer or SVGRenderer is a required step
import {
  CanvasRenderer
} from 'echarts/renderers';
import 'echarts/theme/macarons.js';

echarts.use(
  [TitleComponent, TooltipComponent, GridComponent, LineChart, CanvasRenderer]
);

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    NgxEchartsModule.forRoot({ echarts }),
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Our observation is, image Total Main.js Size: 1.2 MB; Total Network transfer: 1.2MB

So as you could see total size of main.js increased with tree shakable API usage. What could be the reason for the increase in package size after using tree shakable API?

Please let us know in case the approach we followed is not correct.

Thanks Basanth

chris-wahl commented 3 years ago

Having this same problem on Angular 12. Copy-pasted the documentation, and looking at the webpack analysis shows every echarts module is included.

msbasanth commented 3 years ago

@chris-wahl

Yes it looks like when we add theme (downloaded from theme builder) we have echarts included inside that. Here is the comment from echarts https://github.com/apache/echarts/issues/15138

I will keep you updated with the package size.

msbasanth commented 3 years ago

I tried the suggestion from echarts contributor and looks like tree shaking is working in echarts. 👍 https://github.com/apache/echarts/issues/15138

But the same approach of registering the theme ourselves by passing json (basically avoiding require('echarts')) didn't work here with ngx-echarts.

This is what I tried, instead of importing style directly I registered theme manually.

import * as echarts from 'echarts/core';
echarts.registerTheme('dark', { <<Entire theme as json>>});

Even with this network transfer shows same size 1.2MB for the application.

xieziyu commented 3 years ago

@msbasanth Sorry for the late reply and thanks for your issue report. I double checked the codes in https://github.com/xieziyu/ngx-echarts-starter and found the duplicated echarts/theme/macarons.js importing in main.ts. If we remove all the codes importing the theme files directly, the tree shaking should be working as expected.

msbasanth commented 3 years ago

@xieziyu Thanks for looking into this.

tymfear commented 2 years ago

@xieziyu from this issue I didn't really get how to tree shake echarts in Angular. Could you provide some examples and/or explanations other than in linked issues?

msanthoshofficial commented 1 year ago

This issue still seems to be prevailing, any update on this?

chris-wahl commented 1 year ago

@Alphapredator01

Here's the solution I used so that I got both tree-shaking and lazy loading of modules:

  1. I split out the components of interest into charts.ts:
import * as echarts from 'echarts/core';
import {DataZoomComponent, GridComponent, LegendComponent, MarkLineComponent, TitleComponent, ToolboxComponent, TooltipComponent} from 'echarts/components';
import {BarChart, LineChart, SankeyChart} from 'echarts/charts';
import {CanvasRenderer} from 'echarts/renderers';

echarts.use([
  BarChart, LineChart, SankeyChart,
  DataZoomComponent, GridComponent, LegendComponent, MarkLineComponent, TitleComponent, ToolboxComponent, TooltipComponent,
  CanvasRenderer,
]);
export default echarts;
  1. Add an import for this in my app.module.ts:
@NgModule({
  declarations: [AppComponent],
  imports: [
    NgxEchartsModule.forRoot({
      echarts: () => import(
        /* webpackChunkName: "charts" */
        /* webpackMode: "lazy" */
        './path/to/charts').then(m => m.default)
    }),
   .
   .
   .
  bootstrap: [AppComponent]
})
export class AppModule {
}

With my current imports, this gets my gzip'd webpack analysis down to ~204 KB. That used to be closer to 1 MB: image

For testing, I had to make a separate test module file so that it could be imported in unit tests. Created charts-testing.module.spec.ts right next to my charts.ts in step 1:

import echarts from './charts';
import {NgxEchartsModule} from 'ngx-echarts';

/** Simple export to simplify charts import when testing, but allow AppModule to continue to use lazy-loading process */
export const ChartsTestingModuleSpec = NgxEchartsModule.forRoot({echarts});

and then import it like I would any other module in a unittest:


beforeEach( async () => {
    await TestBed.configureTestingModule({
       imports: [
         ChartsTestingModuleSpec,
         NoopAnimationsModule,
          .
          .
          .
        ],
      });
    .
    .
    .
});
semla commented 1 year ago

How would this look for a standalone component?