Tree shaking — How to write code and reduce the bundle size in Webpack 4+

 

Tree shaking has become more popular during recent days and hence now its becomes a vital responsibility for a bundler to support tree shaking.

Okay why it is much more important?. The answer is below,

No user will wait for more than 3 seconds for a page to load.

So its important to keep the bundle size as small as possible for a web page to load fast. Webpack and Rollup are the two popular bundlers which supports tree shaking.

In this article, I am going to speak about some guidelines to write code that support tree shaking and also how to reduce bundle size in Webpack.

Write or compile code to ES6

Since tree shaking works by marking the export and import statements as unused, it is better to write or compile your code to ES6 format. Unused imports will be removed during minification process.

If your browsers does not support ES6 codes then there are many transpiler available to do the conversion (For example: Babel).

Use Feature Module (Loose coupling)

Segregate features into separate modules. Know your customer before planning your web site or library.

All users are not going to use all of your library features, so its always a good practice to segregate the features into separate modules and allowing the user to inject when needed.

The feature modules should be loosely coupled with main module and also ensure to avoid explicit usage of the feature module classes or functions inside main module. Create a pub-sub pattern for your website/library to perform communication between modules.

Learn to use functional programming paradigm

Well, writing code using functional programming paradigm allows you to easily maintain and deliver a bug free product. Below are some of the things, you need to know when using function programming.

  • Write pure class/functions.
  • Use array methods such as filter, some, map etc.
  • Avoid using shared state.
  • Avoid mutating state.

You can find a complete explanation about Function Programming in the Master the JavaScript Interview: What is Functional Programming? drafted by Eric Elliott.

Set sideEffects — Reduce bundle size in Webpack

 

When you have followed the above sections in your code then you are halfway away from enabling proper tree shaking capability for your code.

The last thing, I need to share is the use of settings sideEffects keyword as false in your package.json file. Settings this will inform the webpack that your code/project is safe to exclude the re-exported classes/functions.

Please go through the webpack official documentation for more information on marking your project with sideEffects key.

Conclusion

In this article, we have seen how to use write code that can be tree shaken properly and setting sideEffects keyword in package.json to mark the project as safe to exclude the re-exported codes.

Happy coding.. 🙂

Advertisements

Angular Component styling – Using shadow piercing descendant selector

In this post, I am going to share how the shadow piercing descendant selector helped me with style scoping problem.

Well, I tried to create Angular wrapper for one of a JQuery plugin. All worked as planned and later I noticed that I was not able to apply custom styles to the plugin in ViewEncapsulation.Emulated mode. I can set the custom style when the ViewEncapsulation is set to None.

So I started to search for a solution to apply the custom style in ViewEncapsulation.Emulated mode.

What does ViewEncapsulation emulated mode actually do?

In ViewEncapsulation.Emulated mode, angular will try to emulate the style scoping feature of Shadow DOM. This is achieved by adding attribute to the element and style.

@Component({
    selector: 'my-app',
    template: `
<div>
<h2>Hello {{name}}</h2>
</div>
`,
    styles: [`h2 {
       color: red
    }`]
})
export class App {
   name:string;
   constructor() {
     this.name = `Angular! v${VERSION.full}`
   }
}

Extra attribute will be added to the template elements as follows.

b1

b2

Why component style is not applied to the JQuery plugin?

Inside the plugin the element creation is done by JQuery instead of Angular. So the attributes used to emulate style scoping is not added to the elements.

To illustrate this, check the below code example in which the dynamic h2 element is appended in the ngAfterViewInit lifecycle.


@Component({
    selector: 'my-app',
    template: `
<div>
<h2>Hello {{name}}</h2>
</div>
`,
    styles: [`::ng-deep h2 {
      color: red
    }`]
})
export class App {
     name:string; ele;
     constructor(eleRef: ElementRef) {
       this.name = `Angular! v${VERSION.full}`
       this.ele = eleRef;
     }
     ngAfterViewInit() {
       let dynam = document.createElement('h2');
       dynam.innerHTML = 'Dynamic content ' + this.name;
       this.ele.nativeElement.appendChild(dynam);
     }
}

The result will be as follow, you can see that the style from the component is not applied to the dynamically inserted h2 element.

out
Dynamic element insertion – style is not applied

Shadow piercing descendant selector to the rescue

To resolve the above depicted problem, you can use the shadow piercing descendant selector. It is a technique introduced in shadow DOM concept which is deprecated in major browsers.

But you can use this in Angular using the ::ng-deep special selector. This selector will apply the parent component styles to the descendant child components.

The encapsulation mode should be Emulated to use this feature in Angular

Now the component style should be modified as follows


::ng-deep h2 {
   color: red
}

The result will be as follows

oute
Dynamic element insertion – with ::ng-deep selector

Samplehttps://plnkr.co/edit/qb9m0qglyDNzmizQkC9d?p=preview

Reference

  1. https://angular.io/guide/component-styles#deprecated-deep–and-ng-deep
  2. https://angular.io/guide/component-styles#view-encapsulation

Angular 2 @NgModule.scheme, what does it actually do?

When working with Angular 2 components, sometimes I came up with below error.

1

And googling tells me to set @NgModule.scheme as to CUSTOM_ELEMENTS_SCHEMA which resolve this issue.

So, for what purpose this scheme option is and what does it actually tells the Angular?

The NgModule’s scheme property tells the Angular compiler the type of component it wants to expect from the template. The available schema options are,

  1. CUSTOM_ELEMENTS_SCHEME.
  2. NO_ERRORS_SCHEME.

Default

By default, the compiler will check for Angular components in its template. When the selector is not matched with any of its declarations then it will check for the value of schema property.

If the selector, you used, is an Angular component and still you are getting this issue then you should double check whether you have properly imported the directive/component in the declarations of NgModule as follows.

@Component({
    selector: 'app-test',
    template: `Hello World`
})
export class TestComponent {
    constructor(){}
}

import { AppComponent, TestComponent }
from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    TestComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

CUSTOM_ELEMENTS_SCHEME

This tells the compiler to allow any type of custom element in its template. The custom element should have hypen(-) so it’s attributes.

This helps to incorporate Web components other than Angular component in the application.

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  . . . .
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }

NO_ERRORS_SCHEME
Its simple as the name implies it will not show error when there is an unknown selector in the template.

import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';

@NgModule({
  . . . .
  bootstrap: [AppComponent],
  schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }

.

Happy debugging.. 🙂

Angular 2 – How to get template reference variable or directive instance inside component

In this post let us see how to get Angular 2 directive/child component instance inside the parent component class.

ViewChild in action
The ViewChild decorator can be used to achieve this requirement.
The ViewChild accepts either the directive type or the template reference variable as argument and set the directive instance to the target property after the view get initiated.

@Component({
selector: 'child-cmp',
template: '

child

'
})
class ChildCmp {
doSomething() {}
}
@Component({
selector: 'some-cmp',
template: '<child-cmp></child-cmp>',
directives: [ChildCmp]
})
class SomeCmp {
@ViewChild(ChildCmp) child:ChildCmp;
ngAfterViewInit() {
// child is set
this.child.doSomething();
}
}

The ViewChild will set the target property once the component view get rendered hence accessing the property before the view get initiated will return inappropriate result.

You can use the ngViewAfterInit lifecycle hook to identify the view initialization.

Argument as Directive type vs template variable
Using argument as template variable ViewChild is best from my point of view as it will provide access to specific directive instance when having more than one directive in the template.

@Component({
selector: 'some-cmp',
template: '<child-cmp #child></child-cmp>',
directives: [ChildCmp]
})
class SomeCmp {
@ViewChild('child') child:ChildCmp;
ngAfterViewInit() {
// child is set
this.child.doSomething();
}
}

Happy Scripting.. 🙂