2018年5月27日星期日

Angular6.x 热更新模式配置

前言

因为 angular-cli 默认是不会有热更新的效果,所以需要我们自己做一些配置。官方的 Wiki 更新其实没有完全的跟上,所以按照配置可能也不一定能正确实现,所以这里会将成功的配置说明一下。GitHub

版本

本地的版本信息如下:

Angular CLI: 6.0.5
Node: 8.10.0
OS: darwin x64
Angular: 6.0.3

具体配置

一、需要修改的文件

angular.json
package.json
src/environments/environment.prod.ts
src/environments/environment.ts
src/main.ts

二、需要新增的文件

src/environments/environment.hmr.ts
src/hmr.ts
src/typings.d.ts

三、修改的具体内容以及说明

  • 利用配置分离的方式来配置 hmr 的参数
// environment.prod.ts
export const environment = {
  production: true,
  hmr: false // 把热更新在生产模式关闭,当然你也可以不用,最好是统一配置
};
// environment.ts
export const environment = {
  production: false,
  hmr: false // 跟 prod 意思相同
};
// environment.hmr.ts
export const environment = {
  production: false,
  hmr: true // 开启热更新,这个配置是给 main.ts 代码使用来判断的参数
};

这里就修改好了参数和不同运行环境下的配置了,接下来要配置在不同的启动方式应用不同的配置文件。

  • 映射不同启动环境的配置文件

这部分需要修改 angular.json 文件。

// 1. 修改 JSON 路径:projects/<your_project_name>/architect/build/configurations
// 在这个配置下添加 hmr 属性的配置
// 这里应该看名字很直观,就是在运行的时候把默认配置用 .hmr.ts 文件的内容做替换来启动
"hmr": {
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.hmr.ts"
    }
  ]
}

// 2. 修改 JSON 路径:projects/<your_project_name>/architect/serve/configurations
// 也是添加 hmr 属性的配置
"hmr": {
  "browserTarget": "angular-guide:build:hmr"
}

这里你会想,为什么不把 fileReplacements 配置到 serve 属性下,你可以试试,看下报错信息,会报错的哦~

在这里基本我们配置上就已经完成了,剩下是根据 webpack-hot 规则修改热加载的模块。这里使用最粗暴的形式,在应用最顶端进行热加载(这句话如果不能理解不影响最后结果)。

  • 映射热更新的代码

(1) 代码上有提供 @angularclass/hmr,所以需要先安装一下:

yarn add @angularclass/hmr --dev

(2) 接下来是模板代码,COPY 即可,就是接受热更新模块,直接替换我们应用中的组件模块。

// 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(() => {
    const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef);
    const elements = appRef.components.map(c => c.location.nativeElement);
    const makeVisible = createNewHosts(elements);
    ngModule.destroy();
    makeVisible();
  });
};

(3) 修改 main.ts 的启动代码

const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);
console.log(environment); // 打印看环境文件是否加载正确
console.log('is hot: ', module.hot); // 热更新的参数

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();
}

这里你会发现会提示 module 这个变量不存在,所以需要安装一下全局类型定义 @types/webpack-env

yarn add @types/webpack-env --dev

最后在 src/typings.d.ts 文件中引用类型:

/// <reference types="webpack-env" />

这样就不会提示报错了,因为找到了类型声明文件。

  • 添加 npm script

配置其实是完整了,最后启动命令还是简单点:

"hmr": "ng serve --hmr --configuration=hmr"