เพิ่มความสนุกในการเขียน Angular ด้วย HMR (Hot Module Replacement)

299673_10151471144320809_1119982299_n

ปัญหาในการพัฒนา Angular

ผมเคยฟัง คุณจั๊ว ในหัวข้อ Hyper Productivity ด้วยสมองผมที่มีรอยหยักจำกัดเลยจำอะไรไม่ค่อยได้มากนัก แต่สิ่งที่จำได้ลางๆ คือ

การทำงานมันจะอยู่ในสภาวะไหลลื่น(Flow) นั้นการตอบสนอง(Feedback loop) ต้องสั้นทันใจ

การพัฒนาระบบก็เหมือนกันถ้าเรารู้ว่าสิ่งที่เราแก้ไขไปมันถูกต้องได้ไวขึ้นมันก็น่าจะทำให้ Productivity ดีขึ้นด้วย กลับมาที่ Angular ปัญหาอย่างนึงที่พบเวลาพัฒนาคือเวลาเราแก้ไขอะไรแม้จะเล็กน้อยมันจะต้อง reload ทั้งหน้า ซึ่งปัญหานี้แหละครับที่เราจะเอาเจ้า HMR มาช่วย

เริ่มที่ CLI

ในบทความนี้จะเป็นการใช้ HMR ร่วมกับเครื่องมือยอดนิยมของ Angular นั้นก็คือ Angular CLI นั้นเอง ดังนั้นเริ่มเลยครับ

ng new hmr-demo

hmr-demo-02

หลังจากนั้นก็แวะไปดื่มกาแฟ เข้าห้องน้ำ ดูหน้ากากนักร้องย้อนหลังไปซักแป๊บ ก็จะได้โปรเจค hmr-demo ที่พร้อมว่าแล้วก็ลอง run ซักดอกนึงก่อนครับว่ามันโอเคไหมด้วย

ng serve

hmr-demo-01
โอเคดูเรียบร้อยดีต่อมาเราจะมาเริ่ม configuration กัน

Environment สำหรับ HMR

ปกติเวลาเราพัฒนาระบบเราก็จะมีการแยก environment เป็นไปตามเงื่อนไขต่างๆกันเเช่นเป็น dev, uat หรือ prod ตัว HMR เราก็จะแยก environment ออกมาเหมือนกัน เพราะถ้าเราไป Hot Module replace กับที่ prod ก็อาจจะไม่เหมาะเท่าไหร่ โอเคเรามาเริ่มกัน เริ่มจากสร้างไฟล์ environment.hmr.ts แล้วเพิ่มส่วนของ hmr ลงไปเป็น true อย่างงี้

hmr-demo-03

ส่วน environment อื่นๆที่ไม่ใช่ hmr เราก็จะ set เป็น false ให้หมดแบบนี้เป็นต้น

hmr-demo-04

ต่อมาส่วนที่เราจะไป set เพิ่มเวลาเราเพิ่ม environment ขึ้นมาก็คือ .angular-cli.json ก็ตามนี้เลย

hmr-demo-05

Install ตัว HMR

ต่อมาก่อนที่เราจะใช้ HMR ได้เราก็ต้อง Install มันก่อนก็จาก

npm install --save-dev @angularclass/hmr

จากนั้นเราก็ไปสร้างไฟล์ที่ hmr.ts เพื่อ configuration ตัว HMR กันก็ตามนี้เลย

// src/hmr.ts
import { NgModuleRef, ApplicationRef } from '@angular/core';
import { createNewHosts } from '@angularclass/hmr';

export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef<any>>) => {
  let ngModule: NgModuleRef<any>;
  module.hot.accept();
  bootstrap().then(mod => ngModule = mod);
  module.hot.dispose(() => {
    let appRef: ApplicationRef = ngModule.injector.get(ApplicationRef);
    let elements = appRef.components.map(c => c.location.nativeElement);
    let makeVisible = createNewHosts(elements);
    ngModule.destroy();
    makeVisible();
  });
};

hmr-demo-06

เสร็จแล้วก็ไป set ใน main.ts อีกนิดเพื่อให้ตัว HMR ขึ้นมาทำงานในกรณีที่เรา run ด้วย hmr environment

// src/main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { hmrBootstrap } from './hmr';

if (environment.production) {
  enableProdMode();
}

const bootstrap = () => {
  return platformBrowserDynamic().bootstrapModule(AppModule);
};

if (environment.hmr) {
  if (module['hot']) {
    hmrBootstrap(module, bootstrap);
  } else {
    console.error('HMR is not enabled for webpack-dev-server!');
    console.log('Are you using the --hmr flag for ng serve?');
  }
} else {
  bootstrap();
}

hmr-demo-07
เมื่อเรียบร้อยดีแล้วก็ลองเลยครับ

ng serve --environment=hmr --hmr

ซึ่งถ้าตัว HMR มันทำงานก็จะขึ้นมาบอกประมาณนี้ Hot Module Replacement (HMR) is enabled for the dev server.
hmr-demo-08

พอเราแก้ไข code เช่นแก้ title เป็น Angular Developers Thailand ก็จะเปลี่ยนโดยไม่ต้อง reload ใหม่ทั้งหมด
hmr-demo-09

สรุป

ต้องออกตัวว่ายังไม่เคยลองใช้ในโปรเจคตัวเอง(แต่จะเอาไปลองเดี๋ยวนี้แหละ) แต่มีโปรเจคข้างๆของ คุณเน็ต กูโค้ดใช้อยู่ (ซึ่งก็เป็นคนเอาตัว HMR มาขายผม) แต่ดูแล้วตัว HMR จะทำให้การพัฒนาระบบด้วย Angular รวดเร็วและสนุกขึ้น ถ้าใครลองใช้กันแล้วชอบหรือติดอะไรยังมาแชร์กันได้นะครับ (-/\-)

จบปิ๊ง

เนื้อหาต้นฉบับ: Tutorial: Enable HMR in angular-cli apps