Angular 9 Snake Web Component — 51KB

Goga Koreli
ITNEXT
Published in
6 min readApr 13, 2020

--

With the release of Angular 9 and Ivy, I decided to try and experiment with web components and share my opinion about it.

I use Snake Drive Development, SDD, which essentially means to learn new stack while implementing my favorite project. I have implemented Snake game in many languages and using different patterns. (C++, C#, Angular, Angular via RxJS, React, Angular Web Component). I sincerely recommend learning new tech this way, by implementing favorite project to have fun and learn new things on the way.

This implementation is slightly modified version of my previous implementation of Angular via RxJS Snake Game with few important changes. The changes are:
* Using Flux pattern to manage UI state
* Disable NgZone and use OnPush to reduce bundle size
* Using pure functions and immutable state
* Export everything as a Web Component
* Using RxJS to manage events and trigger Change Detection

There are other tutorials about creating Angular Web Components, which are way more technical. This article is a different in a way that besides technical details, I built quite heavy web component, that uses core Angular features and you can have a look at the final running application’s performance yourself.

Have a look at GitHub repo and Readme for more info

👨‍💻 Angular Snake Web Component GitHub Repository
🎮 Have a look at finished product here

How to create Web Component TL;DR

1. Add elements and polyfill via CLI

This will automatically install @angular/elements package and add document-register-element polyfill

ng add @angular/elements

2. Register Angular Component as Web Component

I created SnakeComponent that imports game logic and renders game state. To register it as a Web Component, have a look at AppModule:

Note that I don’t need AppComponent or Routing, because I plan to only use ng-snake web component, thus I removed other unnecessary stuff from the module declaration to reduce bundle size.

3. Disable NgZone (optional)

It is optional, but to experiment with the Angular’s new functions, I disabled NgZone to take Change Detection in my hands. I used new markDirty pure function introduced with Ivy in V9. Also it reduced bundle size. To disable NgZone use following inside main.ts:

platformBrowserDynamic().bootstrapModule(AppModule, { ngZone: ‘noop’ }).catch(err => console.error(err));

Have a look at following image for markDirty usage example. Essentially, when the game state gets updated, which is Observable, I request markDirty(this) and UI will update accordingly.

4. Build and Deploy

With the help of cli’s ng build --prod js files are generated in the project’s dist folder. There are two versions of files, es5 and es2015. In this article I will be using es2015 version of files main, polyfills and runtime. I combine all of these three files into one snake-web-component.js , which contains Angular runtime, polyfills and game logic as a Web Component. (Note that I don’t use global styles.scss, all of the necessary styles are inside component.scss, thus it gets included inside the final js file)

After build and package, I publish simple index.html and snake-web-component.js on the GitHub repo’s corresponding GitHub Page, with the help of ngh (package’s repo angular-cli-ghpages). GitHub Page serves static files gzipped, that’s why I didn’t gzip final js myself. You can have a look final running application at Angular Snake Web Component GitHub Page.

“build”: “ng build --prod --output-hashing=none”,
package”: “cat dist/snake-web-component/{runtime-es2015,polyfills-es2015,main-es2015}.js > web/snake-web-component.js”,
"deploy": "ngh --dir web",

5. Let’s look at bundle sizes

These are the details I was interested in when I started experimenting with Web Components, to assess how useful they will be in the real world.

Let’s look at 3 important files generated after ng build --prod

  1. main-es2015.js: ~144 KB , ~45 KB (Gzipped)
  2. runtime-es2015.js: ~1 KB, 737 B (Gzipped)
  3. polyfills-es2015.js: ~14 KB, ~6 KB (Gzipped)
  4. Total snake-web-component.js: 159 KB, 51.3 KB (Gzipped)

So, we get 51.3KB js file that contains Angular runtime and game logic by itself inside a Web Component. These are quite good results for the real world scenario.

6. Webpack Bundle Analyzer

Let’s look at some stats through Webpack Bundle Analyzer. We need to build the project without prod flag, to see what packages are used in the build output. This is rough estimate, because prod build will throw away most of the code due to tree shaking, but it is still interesting to get some more information.

We need to run build-stats and then analyze. Inside browser analyzer app will show up and you can have a look at bundle info.

“build-stats”: “ng build --stats-json”,
“analyze”: “webpack-bundle-analyzer dist/snake-web-component/stats-es2015.json”,

This is the screenshot of analyzer. You can see that

  • Angular’s vendor es2015 takes up most of the space, however most of the code will be tree shaken and it will be reduced greatly.
  • Inside vendor you can see that rxjs is taking up some space, that will be greatly reduced after tree shaking too.

The interesting part comes when closely looking at other packages, so lets disable vendor to scale up other files.

  • main-es2015.js: 8.37 KB (Gzipped) contains the game logic and code written by me. You can get rough estimate that in the final snake-web-component.js, this is the actual code written by me.

7. Experiment with Angular Snake Web Component Yourself

Create index.html like shown in gist and you will see Angular Snake Web Component flawlessly running in browser. You can use <ng-snake></ng-snake> anywhere, by simply importing specified js file.

Have a look at game screenshot:

8. Final Thoughts

To summarize Angular Web Components, I think these results are quite promising. The bundle is getting smaller and smaller by only having what is needed. We can start creating libraries and make them available for everyone.
I think there are several new possibilities:

  • Create component library and bundle components in separate files. Consumer of the scripts will import the one js file for the Angular runtime and other separate js files based on their component needs. One Angular runtime will be reused and other components can be imported independently. So that Angular component libraries will be useful not only for Angular developers, but for everyone else.
  • Create full blown component or widget that has it’s independent logic and Angular runtime inside (like my Snake Game Web Component). For example, there can be some widgets or small components that can be embedded anywhere user wants, while using Angular ecosystem, which is great benefit. Also bundle size is quite small already as you can see.

I think this is great news for lots of companies. They can build components instead of applications and make them Web Components. Then embed those Web Components in different applications, so that they have consistency and reusable code, everywhere they embed the Angular Web Component. I am quite positive about this idea and hope to see it used in many places.

Have a look at GitHub repo and Readme for more info

👨‍💻 Angular Snake Web Component GitHub Repository
🎮 Have a look at finished product here

Do you think article is interesting?

Click the 👏 button to spread the article
Follow me on Twitter @gogakoreli

--

--