Angular6项目打包速度优化

公司的angular6项目编译的速度非常的慢,最近查阅资料找到了以下的解决办法:

一:Angular HMR(热模块替换)

Angular HMR(热模块替换):通过HMR的方式实现angular模块级别的热更新,每次只会重新编译和更新修改文件所在的模块,从而降低编译时间。

实现步骤:

  1. 修改environment文件

修改environments/environment.ts文件如下:

export const environment = {
production: false,
hmr: true,
};

修改environments/environment.prod.ts文件如下:

export const environment = {
production: true,
hmr: false,
};

  1. 安装第三方模块
npm install --save-dev @angularclass/hmr
npm install --save-dev @types/node

第一个是用于实现热更新的第三方插件,

第二个是待会需要修改main.ts,其中需要用到module关键字,需要安装@types/node防止启动报错;

  1. 检查 tsconfig.json 文件

安装完@types/node之后需要确保tsconfig.json中,需要确保其中compilerOptions.typeRoots的值中包含node_modules/@types,或者compilerOptions.types的值包含node;

"compilerOptions": {
...
"typeRoots": [
...
"node_modules/@types"
...
]
}
或者
"compilerOptions": {
...
"types": [
...
node
...
]
}
  1. 创建src/hmr.ts文件
import { NgModuleRef, ApplicationRef } from '@angular/core';
import { createNewHosts } from '@angularclass/hmr';

export const hmrBootstrap = (module: any, bootstrap: () => Promise>) => {
  let ngModule: NgModuleRef;
  module.hot.accept();
  bootstrap().then(currentModule => ngModule = currentModule);
  module.hot.dispose(() => {
    const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef);
    const elements = appRef.components.map(c => c.location.nativeElement);
    const removeOldHosts = createNewHosts(elements);
    ngModule.destroy();
    removeOldHosts();
  });
};
  1. 修改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';

const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);

if (environment.production) {
  enableProdMode();
} else {
  if (environment.hmr) {
    // tslint:disable-next-line:no-string-literal
    if (module['hot']) {
      hmrBootstrap(module, bootstrap);
    } else {
      console.log('Amm..HMR is not enabled for webpack');
    }
  } else {
    bootstrap();
  }
}

这是热替换的关键,hmrBootstrap会替换原始的bootstrap(下面会看到), 替换后,当有新的模块更新时,hmr会首先移除掉旧有的模块,然后接收新的模块。这些都是发生在浏览器里面。所以页面不会刷新。

  1. 运行命令
ng serve --hmr

这里替换就得启动文件,如果设置为hmr,那么调用hmrBootStrap来启动网页,否则就用过去的。

  1. 说明

经过验证后发现,该方法只在开发环境修改文件时,重新编译的时候,才能提高速度(因为编译了修改的对应的文件),对于项目首次编译的速度并没有提高,提高首次编译的速度,还需要别的方法。

二:Angular--AOT(预编译)

1. Angular 应用的编译方式

Angular 应用主要由组件及其 HTML 模板组成。由于浏览器无法直接理解 Angular 所提供的组件和模板,因此 Angular 应用程序需要先进行编译才能在浏览器中运行。Angular 提供了两种方式来编译angular应用程序:

  1. 即时编译 (JIT,Just in time),它会在运行期间在浏览器中编译你的应用。
  2. 预先编译(AOT,Ahead of time),它会在构建时编译你的应用。

2. 两种编译模式的区别

JiT编译模式:

一个典型的非AoT应用的开发流程大概是:

  1. 使用TypeScript开发Angular应用
  2. 使用tsc来编译这个应用的ts代码
  3. 打包
  4. 压缩
  5. 部署

一旦把app部署好了,并且用户在浏览器中打开了这个app,下面这些事情会逐一进行:

  1. 浏览器下载js代码
  2. Angular启动
  3. Angular在浏览器中开始JiT编译的过程,例如生成app中各个组件的js代码
  4. 应用页面得以渲染
AoT编译模式:

相对的,使用AoT模式的应用的开发流程是:

  1. 使用TypeScript开发Angular应用
  2. 使用ngc来编译应用
  3. 使用Angular编译器对模板进行编译,生成TypeScript代码
  4. TypesScript代码编译为JavaScript代码
  5. 打包
  6. 压缩
  7. 部署

虽然前面的过程稍稍复杂,但是用户这一侧的事情就变简单了:

  1. 下载所以代码
  2. Angular启动
  3. 应用页面得以渲染
Angular中的Jit和AoT的主要区别
  1. 编译过程发生的时机
  2. JiT生成的是JS代码,而AoT生成的是TS代码。这主要是因为JiT是在浏览器中进行的,它完全没必要生成TS代码,而是直接生产了JS代码。

使用JIT和AOT编译的项目打包速度差别如下图所示:

image

3. 如何使用AOT编译

注:当你运行 ng build(仅编译)或 ng serve(编译并启动本地服务器) 这两个 CLI 命令时 JIT 编译是默认选项;要进行 AOT 编译,只要让 ng build 或 ng serve 命令中包含 --aot 标志。

带有 --prod 标志的 ng build 命令 (ng build --prod) 会默认使用 AOT 编译。

AOT:在浏览器下载和运行代码之前的编译阶段,Angular 预先(AOT)编译器会先把你的 Angular HTML 和 TypeScript 代码转换成高效的 JavaScript 代码。

4. 使用AOT编译的好处

好处如下:

  1. 渲染得更快:使用 AOT浏览器下载预编译版本的应用程序。浏览器直接加载运行代码,所以它可以立即渲染该应用,而不用等应用完成首次编译;
  2. 需要的异步请求更少:编译器把外部 HTML 模板和 CSS 样式表内联到了该应用的 JavaScript 中。 消除了用来下载那些源文件的 Ajax 请求;
  3. 需要下载的 Angular 框架体积更小:如果应用已经编译过了,自然不需要再下载 Angular 编译器了。 该编译器差不多占了 Angular 自身体积的一半儿,所以,省略它可以显著减小应用的体积;
  4. 提早检测模板错误:AOT 编译器在构建过程中检测和报告模板绑定错误,避免用户遇到这些错误;
  5. 更安全:AOT 方式会在发给客户端之前就把 HTML 模板和组件编译成 JavaScript 文件。 不需要读取模板,也没有客户端组装 HTML 或执行 JavaScript 的危险操作,受到注入类攻击的机会也比较少。

三: 禁止Rollup 摇树优化

所谓Rollup是指Webpack2会把那些应用中未使用的引用代码除掉,但不会删除这些代码,所以就需要配合 UglifyJs 能够智能的移除这些未使用的代码。从而减少包体大小。

而Agnular应用是基于Typescript,因此Angular Cli提供了一个叫 Angular Build Optimizer 插件,将 Typescript 编译结果转化成更友好的UglifyJs版本。这样UglifyJs就能够更有效的移除那些未使用的代码。

Angular Cli只需要加上 --build-optimizer 参数就可以,这将禁用供应商数据块,并导致代码更小。在一些情况下压缩的还是很厉害的。

ng build --prod --build-optimizer

这是删除未使用的代码的过程,从而减小了构建大小。如果您使用angular-cli,则默认情况下会启用Tree-Shaking。但是虽然减小了构建大小(只会减小几kb),打包的速度却增加了将近一倍,使用以下的编译,能有效的缩短项目的打包时间:

ng build --prod --build-optimizer=false

使用摇树优化和不使用摇树优化的项目打包速度差别如下图所示:

image

说明:牺牲时间换取空间,还是牺牲空间换取时间,就看个人选择了。

四:使用webpack 4.0以上的版本构建

五:升级@angular-devkit/build-angular

六:gzip压缩

七:升级到Angular 8并使用差分加载

八:RXJS

九:thread-loader插件使用

原理

把这个 loader 放置在其他 loader 之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行:

在 worker 池(worker pool)中运行的 loader 是受到限制的。例如:

  • 这些 loader 不能产生新的文件。
  • 这些 loader 不能使用定制的 loader API(也就是说,通过插件)。
  • 这些 loader 无法获取 webpack 的选项设置。
  • 每个 worker 都是一个单独的有 600ms 限制的 node.js 进程,即一个worker 就是一个nodeJS 进程【node.js proces】,每个单独进程处理时间上限为600ms,同时跨进程的数据交换也会被限制在这个时间之内,即各个进程的数据交换会限制在这个时间内。

所以,请仅在耗时的 loader 上使用。

使用步骤

  1. 安装:
    npm install --save-dev thread-loader
  2. 在webpack.config.js文件中:
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve("src"),
        use: [
          "thread-loader",
          "expensive-loader"
        ]
      }
    ]
  }
}

可配选项

use: [
  {
    loader: "thread-loader",
    // 有同样配置的 loader 会共享一个 worker 池(worker pool)
        options: {
      // 产生的 worker 的数量,默认是 cpu 的核心数
      workers: 2,

      // 一个 worker 进程中并行执行工作的数量
      // 默认为 20
      workerParallelJobs: 50,

      // 额外的 node.js 参数
      workerNodeArgs: ['--max-old-space-size', '1024'],

      // 闲置时定时删除 worker 进程
      // 默认为 500ms
      // 可以设置为无穷大, 这样在监视模式(--watch)下可以保持 worker 持续存在
      poolTimeout: 2000,

      // 池(pool)分配给 worker 的工作数量
      // 默认为 200
      // 降低这个数值会降低总体的效率,但是会提升工作分布更均一
      poolParallelJobs: 50,

      // 池(pool)的名称
      // 可以修改名称来创建其余选项都一样的池(pool)
      name: "my-pool"
    }
  },
  "expensive-loader"
]

预热:

可以通过预热 worker 池( worker pool )来防止启动 worker 时的高延时。

这会启动池 ( pool ) 内最大数量的 worker 并把指定的模块载入 node.js 的模块缓存中。

const threadLoader = require('thread-loader');

threadLoader.warmup({
  // pool options, like passed to loader options
  // must match loader options to boot the correct pool
}, [
  // modules to load
  // can be any module, i. e.
  'babel-loader',
  'babel-preset-es2015',
  'sass-loader',
]);

参考文件:

  1. angular 开发编译速度慢,影响开发体验:https://www.jianshu.com/p/657c28648db8
  2. Angular HMR(热模块替换) 功能实现:https://www.jianshu.com/p/281e5aa559b0
  3. Angular官方文档:https://angular.cn/cli/build
  4. Angular--AOT和JIT两种编译方式带来的改变:https://www.cnblogs.com/robinw666/p/11725261.html
  5. 译文 | Angular中的AoT编译:https://www.jianshu.com/p/6cd8f09bc03f
  6. https://medium.com/faun/44-quick-tips-to-fine-tune-angular-performance-9f5768f5d945

你可能感兴趣的:(Angular6项目打包速度优化)