Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Ionic Angular standalone component feedback #28445

Closed
3 tasks done
hakimio opened this issue Nov 1, 2023 · 6 comments
Closed
3 tasks done

feat: Ionic Angular standalone component feedback #28445

hakimio opened this issue Nov 1, 2023 · 6 comments
Labels

Comments

@hakimio
Copy link

hakimio commented Nov 1, 2023

Prerequisites

Describe the Feature Request

I have recently tested the new Ionic standalone components and found out that not only the production bundle increased but the DX became worse as well.

Build Stats

Webpack builds with IonicModule (vanilla experience):

Bundle size:

Initial Chunk Files                   | Names                                    |   Raw Size | Estimated Transfer Size
main.226927d94d05f13e.js              | main                                     |    1.11 MB |               293.85 kB
styles.65dcb624a7f5532c.css           | styles                                   |   38.14 kB |                 7.43 kB
polyfills.5881976821f2e6c5.js         | polyfills                                |   33.10 kB |                10.69 kB
runtime.5a40e30ca595c559.js           | runtime                                  |    4.71 kB |                 2.26 kB

                                      | Initial Total                            |    1.19 MB |               314.22 kB

Build time without cache: 64.56s
Build time with cache: 21.75s

Webpack builds with Ionic standalone components (all imports from @ionic/angular/standalone)

Bundle size:

Initial Chunk Files           | Names                                    |   Raw Size | Estimated Transfer Size
main.06cb55aa5358d0f2.js      | main                                     |    1.41 MB |               327.58 kB
styles.65dcb624a7f5532c.css   | styles                                   |   38.14 kB |                 7.43 kB
polyfills.5881976821f2e6c5.js | polyfills                                |   33.10 kB |                10.69 kB
runtime.c69058a04b0bc7f8.js   | runtime                                  |    4.33 kB |                 2.09 kB

                              | Initial Total                            |    1.48 MB |               347.79 kB

Build time without cache: 65.59s
Build time with cache: 24.45s

Esbuild build with Ionic standalone components

Bundle size:

Initial Chunk Files   | Names         |   Raw Size | Estimated Transfer Size
chunk-VCZIZF5F.js     | -             |    1.54 MB |               317.82 kB
styles.MTDGK4LK.css   | styles        |   38.72 kB |                 7.45 kB
polyfills.YOD2V263.js | polyfills     |   32.89 kB |                10.72 kB
chunk-HKJCHXVW.js     | -             |   32.64 kB |                10.87 kB
chunk-VFY3GGQ6.js     | -             |   30.68 kB |                 9.96 kB
chunk-F2DNOSG5.js     | -             |   17.69 kB |                 3.48 kB
chunk-ZVEDURUM.js     | -             |   11.01 kB |                 3.95 kB
chunk-7EFNZMO4.js     | -             |    9.88 kB |                 2.56 kB
chunk-IAWNAUG6.js     | -             |    9.88 kB |                 2.56 kB
chunk-TDR7JFZA.js     | -             |    8.61 kB |                 3.15 kB
main.WFGZLYL2.js      | main          |    7.80 kB |                 2.40 kB
chunk-QSJGIY2Z.js     | -             |    7.79 kB |                 2.37 kB
chunk-MDMZPGPE.js     | -             |    5.68 kB |                 2.10 kB
chunk-UDVSIRTF.js     | -             |    3.78 kB |                 1.42 kB
chunk-GJ7P76DY.js     | -             |    1.50 kB |               662 bytes
chunk-SS7U44WW.js     | -             |    1.05 kB |               531 bytes
chunk-B43CU5JX.js     | -             | 1022 bytes |              1022 bytes
chunk-SVE7U3VL.js     | -             |  946 bytes |               946 bytes
chunk-WLLYNIW2.js     | -             |  941 bytes |               941 bytes
chunk-IUKXAO2M.js     | -             |  678 bytes |               678 bytes
chunk-TQEIIVVC.js     | -             |  541 bytes |               541 bytes
chunk-SQ4HSFGE.js     | -             |  469 bytes |               469 bytes
chunk-4WFVMWDK.js     | -             |  103 bytes |               103 bytes
chunk-LF5XB4YN.js     | -             |   99 bytes |                99 bytes
chunk-4SXIAE7V.js     | -             |    0 bytes |                 0 bytes

                      | Initial Total |    1.76 MB |               386.65 kB

Build time without cache: 24.44s

Conclusion

Bundle size ballooned from 1.19 MB to 1.76 MB (almost 50% increase), cold production build time decreased from 64.56s to 24.44s (~2.6x decrease).

General experience with Ionic standalone components

Cons

  • Ionic team does not provide automatic migration schematics, so it's extremely time consuming to manually migrate everything.
  • Ionic does not export standalone component groups, so you have to import all the components one-by-one, which causes a huge increase of Ionic component count in the standalone component imports list. My suggestion would be to export component groups like the following:
export const ION_ACCORDION_COMPONENTS = [IonAccordionGroup, IonAccordion, IonItem, IonLabel];
export const ION_CARD_COMPONENTS = [IonCardHeader, IonCardSubtitle, IonCardTitle, IonCardContent, IonCard];
export const ION_LIST_COMPONENTS = [IonList, IonItem, IonListHeader, IonLabel];
export const ION_TOOLBAR_COMPONENTS = [IonToolbar, IonButtons, IonButton, IonTitle];
  • The new addIcons() DX is just plain terrible. Not only you have to remember to manually import all the icons, but, when you forget to import some, you get a non-descript error message like the following:
    image
    My suggestion would be to get rid of addIcons() and instead add a config to ionic.config.json called icons, where you can list all the icons you want to use. If the user does not provide this config, default to importing all icons like before.
  • Significant increase in production bundle size.

Pros

  • Faster dev server rebuilds when using esbuild builder. It's a bit faster compared to webpack builds without HMR (ng serve --hmr), but still slower compared to webpack HMR builds.
  • Considerably faster cold production builds when using the new esbuild builder.

Describe the Use Case

Better DX.

Describe Preferred Solution

No response

Describe Alternatives

No response

Related Code

No response

Additional Information

Small tip for people who want to try the new esbuild builder. If you get errors like the following after switching to esbuild:

X [ERROR] Can't find stylesheet to import.
  ╷
1 │ @import '/src/theme/sass-variables';
  │         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ╵

You have to update your stylePreprocessorOptions in the builder options:

image

@ionitron-bot ionitron-bot bot added the triage label Nov 1, 2023
@LennonReid
Copy link

I'd like to discuss the @ionic/angular version I'm using. With the adoption of v7.5.3-nightly, I've observed a noticeable reduction in my app's bundle size, all thanks to the standalone feature.

Regarding 'addIcon', I think it's an effective means to distribute and manage icons, aligning perfectly with the core concept of the "standalone" approach.

However, it's worth noting that despite the benefits, I'm still sticking with v7.5.2 for everything except icons. This decision is primarily driven by the ongoing issues that persist within the standalone approach.

By the way, this issue could serve as valuable feedback for further improvement.

@liamdebeasi
Copy link
Contributor

Hey there! Thanks for trying the standalone build of Ionic. There are several pieces of feedback here, so I'll reply to each individually.

  1. The bundle size has increased when using standalone

Ionic uses a tool provided by the Angular CLI called ng-packagr to bundle Ionic Angular. There was an unfortunate bug in ng-packagr discovered that caused treeshaking to not work correctly. This issue has been resolved in Ionic 7.5.3 (released a few moments ago).

I will also note that ESBuild support is experimental in Angular, so I would expect there to be some issues with that. I recommend providing feedback to the Angular team for issues here since we do not control the ESBuild configuration.

If you are still running into treeshaking issues with a Webpack configuration, please open a separate issue with a GitHub repo we can use to reproduce the issue. Treeshaking issues can be tricky, so having a separate thread to discuss will be useful if you are still running into problems.

  1. Ionic does not provide automatic migration for standalone

We actually do provide (experimental) support for migration. You can access it here: https://github.com/ionic-team/ionic-angular-standalone-codemods

Since it's experimental, we are rolling it out slowly. We plan to publicize it more in the future. This tool won't migrate everything, but it's designed to assist with some of the more tedious parts of the migration.

  1. Ionic does not provide export groups for components

I'm not sure I really understand the use case for this. Importing exactly the components you need is by design, so having import groups would mean you are potentially importing more than you need. It also requires the developer to know exactly which components are contained in the import group which can be confusing. For example, an "accordion group" is pretty straightforward, but what should go into a "toolbar group" is fairly subjective.

  1. Having to manually import the icons is tedious

This is a required process in order to have proper treeshaking and aligns with how other Angular icon libraries work (such as https://ng-icons.github.io/ng-icons/#/). If developers do not define the icons they want to use, then the bundler has no way of knowing which icons should be pulled into the build. As a result, the bundler will pull in every single icon, and the bundle size would increase drastically. The migration tool is designed to assist with registering icons as well.

You don't have to call addIcons in every component. You're more than welcome to register them in main.ts or app.component.ts. You can then use them anywhere in your application. However, the initial bundle size may increase because the icons need to be loaded up front. This isn't something that is very clear in the documentation, so I opened #28445 to clarify this.

  1. The icon error message is confusing

I agree that the icon error messaging could be improved. I opened ionic-team/ionicons#1297 to address this.


I am going to close this since we use this tracker for bug issues and feature requests, but please feel free to reply and I'll be more than happy to continue the conversation. As for the documentation and icon improvements, you can follow the linked thread for updates on that work.

@hakimio
Copy link
Author

hakimio commented Nov 1, 2023

@liamdebeasi thank you for taking the time to respond to my feedback 🙂

There was an unfortunate bug in ng-packagr discovered that caused treeshaking to not work correctly. This issue has been resolved in Ionic 7.5.3

Just tested 7.5.3 and it's not looking good. The webpack bundle size is the same like before (around ~24% increase), but esbuild build size has now increased from 1.76 MB (ionic 7.5.2) to 2.09 MB (7.5.3). That's almost double of original 1.19 MB. Something must be wrong.
Anyway, can't you just test the bundle size with some of your demo apps?

We actually do provide (experimental) support for migration.

Awesome. That will help a lot. Do you plan to announce this new tool anytime soon?

an "accordion group" is pretty straightforward, but what should go into a "toolbar group" is fairly subjective.

I agree, that the toolbar group was a bad example, but I still think card, accordion, list, tabs component groups make perfect sense. How do you know what's inside the group? Just check the docs:
image

You don't have to call addIcons in every component. You're more than welcome to register them in main.ts or app.component.ts.

I didn't know. That makes it a lot easier to use. I think mentioning that in the docs will help a lot of other users as well.

I agree that the icon error messaging could be improved. I opened ionic-team/ionicons#1297 to address this.

Thank you.

@liamdebeasi
Copy link
Contributor

Anyway, can't you just test the bundle size with some of your demo apps?

We do test our demo apps, and treeshaking works well there. Unfortunately, there are many ways in which treeshaking can fail (side effects can cause unexpected things to be imported). Can you open a separate issue with a demo? I can take a look.

Awesome. That will help a lot. Do you plan to announce this new tool anytime soon?

Yep! I just updated the announcement blog with a link. I also opened ionic-team/ionic-docs#3223 to publicize the migration utility on the documentation.

I agree, that the toolbar group was a bad example, but I still think card, accordion, list, tabs component groups make perfect sense. How do you know what's inside the group? Just check the docs:

That's part of my concern for this. I don't think developers should have to constantly reference the documentation website whenever they want to import a component from Ionic.

That being said, you should be able to create import groups in your own application which give you a similar result. I think this is a better approach because it allows developers to define groups in a way that makes sense for them/their team.

src/import-groups.ts

import { IonAccordion, IonAccordionGroup, IonInput, IonTextarea } from '@ionic/angular/standalone';

export const ACCORDION_GROUP = [IonAccordion, IonAccordionGroup];
export const INPUT_GROUP = [IonInput, IonTextarea];

src/home/home.page.ts

import { Component } from '@angular/core';
import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
import { INPUT_GROUP } from '../../import-groups';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  standalone: true,
  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ...INPUT_GROUP],
})
export class HomePage {
  constructor() {}
}

@liamdebeasi liamdebeasi removed their assignment Nov 1, 2023
@hakimio
Copy link
Author

hakimio commented Nov 6, 2023

@liamdebeasi

Can you open a separate issue with a demo? I can take a look.

I'll try to crate a simple reproduction ASAP.

I just updated the announcement blog with a link. I also opened ionic-team/ionic-docs#3223 to publicize the migration utility on the documentation.

Awesome 🙂

That's part of my concern for this. I don't think developers should have to constantly reference the documentation website whenever they want to import a component from Ionic.

Exporting standalone component groups is an official Angular recommendation for library creators and as it wouldn't be mandatory to use the groups instead of importing single components, I think it would only add value to the users, but yes, I can just as easily create the groups myself.

imports: [IonContent, ...INPUT_GROUP],

Small tip: you can import an array of components in both modules and standalone components without having to use the spread operator:

imports: [IonContent, INPUT_GROUP],

Same applies to providers as well.

Copy link

ionitron-bot bot commented Dec 6, 2023

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Dec 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants