Angular6项目体积打包优化

4,746 阅读6分钟

可以从以下四个角度优化:

  1. 模块懒加载
  2. 输出包体组成分析文件
  3. Rollup 摇树优化
  4. 导出Webpack配置,通过修改webpack配置优化打包

让我们来逐一分析。

模块懒加载

默认情况下,在初始化的时候就把所有路由给加载完了,那么多页面,就这样一次性加载完,导致页面加载非常慢,Angular中提出了用懒加载来加载。

通俗的讲就是说进入主模块的时候,子模块不加载,等需要进入子模块的时候才加载。项目划分模块的时候,使用loadChildren配置路由是最佳选择方案。

关键知识点

  1. RouterModule.forRoot() 和 RouterModule.forChild()

    RouterModule对象为提供了两个静态的方法:forRoot()和forChild()来配置路由信息。

    RouterModule.forRoot()方法用于在主模块中定义主要的路由信息. RouterModule.forChild()与 Router.forRoot()方法类似,但它只能应用在特性模块(子模块)中。

    即根模块中使用forRoot(),子模块中使用forChild()。

  2. 懒加载:loadChildren

    这里使用到了懒加载LoadChildren属性。这里没有将对应的模块导入到AppModule中,而是通过loadChildren属性,告诉Angular路由依据loadChildren属性配置的路径去加载对应的模块。这就是模块懒加载功能的具体应用,当用户访问 /xxx/**

    路径的时候,才会加载对应的模块,这减少了应用启动时加载资源的大小。

    loadChildren的属性值由三部分组成:

    1). 需要导入模块的相对路径

    2). #分隔符

    3). 导出模块类的名称

懒加载的实现

1. 创建项目
ng new angularLaod --routing (生成默认带有routing module 的项目)
执行 --routing会执行以下步骤
1. 创建app-routing.module.ts
2. 将app-routing.module.ts导入到app.module.ts
3. 在app.component.html添加<router-outlet></router-outlet>
2. 生成子模块
ng g m childrenLaod --routing
3. 在组件中声明component
ng g c children-load/child
在children-load-routing中添加路由默认路径
import {ChildComponent} from './child/child.component';

const routes: Routes = [
	{path:'',component:ChildComponent}
];
重复步骤 2、3、4生成children-newload
4. 在app-routing.module模块中导入children-load模块
const routes: Routes = [
  {path:'',redirectTo:'child'},
  {path:'child',loadChildren:'./children-load/children-load.module#ChildrenLoadModule'},
  {path:'childNew',loadChildren:'./children-newload/children-newload.module#ChildrenNewloadModule'}
];
注意:LoadChildren必须遵循 相对路径+#+module名称
(参考:https://blog.csdn.net/idomyway/article/details/84996458)

输出包体组成分析文件

Webpack 有一个非常好用的工具叫 webpack-bundle-analyzer,会自动分析包体组成结构,并以一种可视化的方式显示。

使用步骤:

  1. 打开项目,命令行输入:npm install webpack-bundle-analyzer --save-dev
  2. 命令行输入项目打包命令加--stats-json
  3. 在package.json文件的"scripts"里配置里,添加"bundle-report": "webpack-bundle-analyzer dist/wp/stats.json"
  4. 命令行输入:npm run bundle-report
  5. 浏览器输入:http://127.0.0.1:8888/ 查看分析图,根据分析图了解一些优化的细节

在查看分析图的过程中,我发现了以下几点:

  1. 在不影响性能的情况下,尽量少用Base64引入图片,把图片放在本地引入,不会打包到项目中去,但使用Base64编码的图片,会以图片的1.5倍体积打包到项目中去,且若该图片为精灵图,使用图片时采取了div切图,切了几次,就会打包几次,占用体积非常大。具体可以参考以下:www.imooc.com/article/278…

  2. 尽量使用import引入依赖,而不是require。原因如下:

    require的核心概念:在导出的文件中定义module.exports,导出的对象类型不予限定(可为任意类型)。在导入的文件中使用require()引入即可使用。

    本质上,是将要导出的对象,赋值给module这个对象的exports属性,在其他文件中通过require这个方法来访问exports这个属性。上面b.js中,require(./a.js) = exports 这个对象,然后使用es6取值方式从exports对象中取出test的值。

    import是es6为js模块化提出的新的语法,import (导入)要与export(导出)结合使用。

    commonjs模块与ES6模块的区别:

    (1). commonjs输出的,是一个值的拷贝,而es6输出的是值的引用;

    (2). commonjs是运行时加载,es6是编译时输出接口。

    具体请参考:blog.csdn.net/liya_nan/ar…

Rollup 摇树优化

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

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

方法一:Angular Cli只需要在打包命令中加上 --build-optimizer 参数就可以,在一些情况下压缩的还是很厉害的(但我做优化的过程中,使用了这样的方法,毫无作用)
方法二:去掉没有用到的模块,从而减小体积(此方法对于我优化的项目毫无作用)
  1. moment

    • 对于 ng-cli 的项目:

      (1)运行npm uninstall moment

      (2)运行npm install moment --save-dev

      (3).angular-cli.json 文件的 scripts 里加上"../node_modules/moment/min/moment.min.js"

      (4)typings.d.ts 文件的最后加上 declare var moment: any;

      (5)将项目中的代码 import * as moment from 'moment';全部干掉

      备注:如果想应用其中某个库则需要(以 fr 为例)

      引入:import "moment/locale/fr";

      使用:moment.locale("fr");

    • 对于 webpack 的项目:

      (1)在 webpack 配置文件的 plugins 里加上

      new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, / /) 或者

      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)

      备注:如果想应用其中某个库则需要(以 de、fr、hu 为例),在 webpack 配置文件的 plugins 里加上

      new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /de|fr|hu/)

    备注:

    我查了一些资料了解到,如果项目中只用到了moment的方法,那么可以按需引入,如果用到的是moment实例对象,那么只能引入整个moment对象。

  2. @ng-bootstrap/ng-bootstrap

    如果在项目中要用到 @ng-bootstrap/ng-bootstrap 库,要注意一下使用方法,如果按照其官网示例方式引入的话,在打包的时候会把其下所有模块引入进来,不管你用没用到。所以要想办法把没有用到的模块干掉,只引入需要的模块。

    以时间组件为例,官网示例:

    // 根模块
    import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
    
    @NgModule({
      declarations: [AppComponent, ...],
      imports: [NgbModule.forRoot(), ...],  
      bootstrap: [AppComponent]
    })
    export class AppModule {
    }
    
    // 其他需要模块
    import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
    
    @NgModule({
      declarations: [OtherComponent, ...],
      imports: [NgbModule, ...],
    })
    export class OtherModule {
    }
    

    更改为:

    // 根模块
    import {NgbDatepickerModule, NgbTimepickerModule} from '@ng-bootstrap/ng-bootstrap';
    
    @NgModule({
      declarations: [AppComponent, ...],
      imports: [NgbDatepickerModule.forRoot(), NgbTimepickerModule.forRoot() ...],  
      bootstrap: [AppComponent]
    })
    export class AppModule {
    }
    
    // 其他需要模块
    import {NgbDatepickerModule, NgbTimepickerModule} from '@ng-bootstrap/ng-bootstrap';
    
    @NgModule({
      declarations: [OtherComponent, ...],
      imports: [NgbDatepickerModule, NgbTimepickerModule, ...],
    })
    export class OtherModule {
    }
    

导出Webpack配置

方法一:eject(Angular6可能不支持)
方法二:工具库ngx-build-plus

我期望通过导出Webpack配置的方式,使scss文件与main.js打包分离,仍在实践中,成功后续更。

参考文章:

  1. www.mamicode.com/info-detail…
  2. www.imooc.com/article/278…
  3. blog.csdn.net/liya_nan/ar…