MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Webpack与Angular框架的构建流程解析

2021-09-233.2k 阅读

Webpack 基础概念

Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

核心概念

  1. 入口(Entry):入口指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
    // webpack.config.js
    module.exports = {
        entry: './src/index.js'
    };
    
  2. 输出(Output):output 告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 目录中。
    module.exports = {
        entry: './src/index.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'bundle.js'
        }
    };
    
  3. 加载器(Loader):webpack 本身只能理解 JavaScript 和 JSON 文件,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。例如,css-loader 用于加载 CSS 文件,babel-loader 用于将 ES6+ 代码转换为 ES5 代码,以兼容旧版浏览器。
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader']
                }
            ]
        }
    };
    
  4. 插件(Plugin):插件用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件的使用是在 plugins 数组中指定。例如,HtmlWebpackPlugin 可以自动生成 HTML 文件,并将打包后的 JavaScript 文件插入其中。
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        plugins: [
            new HtmlWebpackPlugin({
                template: './src/index.html'
            })
        ]
    };
    

Angular 框架概述

Angular 是一款由 Google 维护的开源 JavaScript 框架,用于构建高效、复杂的单页应用程序(SPA)。它采用了组件化的架构,使得代码的组织和维护更加容易。

核心特性

  1. 组件化架构:Angular 应用由组件组成,每个组件负责应用的一个特定部分。组件有自己的模板、样式和逻辑。例如,一个简单的 Angular 组件可能如下:
    import { Component } from '@angular/core';
    
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html',
        styleUrls: ['./app.component.css']
    })
    export class AppComponent {
        title = 'My Angular App';
    }
    
  2. 模板语法:Angular 使用一种独特的模板语法来绑定数据、处理用户输入和控制 DOM 渲染。例如,使用插值语法将组件中的数据显示在模板中:
    <!-- app.component.html -->
    <h1>{{title}}</h1>
    
  3. 依赖注入(DI):依赖注入是 Angular 中的一项重要特性,它允许将依赖关系(如服务)注入到组件或其他类中,而不是在类内部创建依赖的实例。这使得代码更易于测试和维护。例如,创建一个简单的服务并注入到组件中:
    // hero.service.ts
    import { Injectable } from '@angular/core';
    
    @Injectable({
        providedIn: 'root'
    })
    export class HeroService {
        getHeroes() {
            return ['Superman', 'Batman', 'Wonder Woman'];
        }
    }
    
    // app.component.ts
    import { Component } from '@angular/core';
    import { HeroService } from './hero.service';
    
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html'
    })
    export class AppComponent {
        heroes: string[];
        constructor(private heroService: HeroService) {
            this.heroes = this.heroService.getHeroes();
        }
    }
    

Angular 项目中的 Webpack 构建流程

  1. 初始化 Angular 项目:首先,通过 Angular CLI 初始化一个新的 Angular 项目。确保你已经全局安装了 Angular CLI,然后执行以下命令:

    ng new my - app
    

    这会创建一个新的 Angular 项目结构,其中包含 src 目录用于存放应用代码,angular.json 文件用于配置项目。

  2. Webpack 配置:Angular CLI 默认使用 Webpack 进行构建。angular.json 文件中的 architect.build 部分包含了 Webpack 的相关配置。例如,builder 字段指定了使用 @angular - cli:browser,它实际上是基于 Webpack 的构建器。

    {
        "architect": {
            "build": {
                "builder": "@angular - cli:browser",
                "outputPath": "dist/my - app",
                "index": "src/index.html",
                "main": "src/main.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "src/tsconfig.app.json",
                "assets": [
                    "src/favicon.ico",
                    "src/assets"
                ],
                "styles": [
                    "src/styles.css"
                ],
                "scripts": []
            }
        }
    }
    
  3. Entry 配置:在 Angular 项目中,入口文件通常是 src/main.ts。这个文件负责引导 Angular 应用的启动。它会导入 platformBrowserDynamic 并使用它来引导 AppModule

    import { platformBrowserDynamic } from '@angular/platform - browser - dynamic';
    import { AppModule } from './app/app.module';
    
    platformBrowserDynamic().bootstrapModule(AppModule)
      .catch(err => console.error(err));
    
  4. 加载器配置:Angular CLI 已经为常见的文件类型配置了加载器。对于 TypeScript 文件,使用 @angular - cli:typescript 加载器,它会处理 TypeScript 代码的编译和转换。对于 CSS 和 SCSS 文件,也有相应的加载器配置。例如,对于 CSS 文件:

    {
        "architect": {
            "build": {
                "styles": [
                    "src/styles.css"
                ],
                "stylePreprocessorOptions": {
                    "includePaths": []
                }
            }
        }
    }
    

    这里,styles 数组指定了要包含的 CSS 文件,而 stylePreprocessorOptions 可用于配置像 SCSS 等预处理语言的选项。

  5. 插件配置:Angular CLI 会自动添加一些必要的 Webpack 插件。例如,HtmlWebpackPlugin 用于生成 HTML 文件并将打包后的 JavaScript 文件插入其中。这个插件的配置部分隐藏在 @angular - cli:browser 构建器内部。不过,如果需要自定义插件,可以使用 @angular - cli:webpack 构建器,并在项目根目录下创建 webpack.extra.js 文件来扩展 Webpack 配置。例如,要添加 OptimizeCSSAssetsPlugin 来压缩 CSS:

    const OptimizeCSSAssetsPlugin = require('optimize - css - assets - plugin');
    
    module.exports = {
        plugins: [
            new OptimizeCSSAssetsPlugin({})
        ]
    };
    

    然后在 angular.json 中修改 architect.build.builder@angular - cli:webpack,并添加 webpackConfig 字段指向 webpack.extra.js

    {
        "architect": {
            "build": {
                "builder": "@angular - cli:webpack",
                "outputPath": "dist/my - app",
                "index": "src/index.html",
                "main": "src/main.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "src/tsconfig.app.json",
                "assets": [
                    "src/favicon.ico",
                    "src/assets"
                ],
                "styles": [
                    "src/styles.css"
                ],
                "scripts": [],
                "webpackConfig": {
                    "path": "./webpack.extra.js"
                }
            }
        }
    }
    
  6. 构建过程:当执行 ng build 命令时,Angular CLI 会根据 angular.json 中的配置,调用 Webpack 进行构建。Webpack 首先从入口文件 main.ts 开始,解析其依赖关系,通过加载器处理各种文件类型,然后使用插件进行优化和生成最终的输出。在构建过程中,Webpack 会将 TypeScript 代码编译为 JavaScript,将 CSS 和其他资源打包,并生成最终的 HTML 文件,其中包含对打包后 JavaScript 和 CSS 文件的引用。

优化 Angular 项目的 Webpack 构建

  1. 代码分割:Webpack 支持代码分割,这对于 Angular 应用来说非常有用,可以将应用代码分割成更小的块,按需加载,提高应用的加载性能。Angular CLI 已经对懒加载模块进行了代码分割的支持。例如,当创建一个懒加载模块时:
    ng generate module admin --route=admin --module=app.module
    
    这会创建一个 AdminModule,并配置为懒加载。Webpack 会将这个模块的代码分割成单独的文件,只有在用户访问 admin 路由时才会加载。
  2. Tree - shaking:Tree - shaking 是一种优化技术,它可以去除未使用的代码。在 Angular 项目中,由于使用了 ES6 模块,Webpack 可以自动进行 Tree - shaking。确保在项目中使用 ES6 模块语法导入和导出,并且尽量减少全局变量和副作用代码,这样 Webpack 就能更有效地进行 Tree - shaking。
  3. 优化图片加载:对于图片资源,可以使用 image - webpack - loader 来压缩图片。首先安装该加载器:
    npm install image - webpack - loader --save - dev
    
    然后在 webpack.extra.js 中添加如下配置:
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: 'file - loader',
                            options: {
                                name: 'images/[name].[ext]'
                            }
                        },
                        {
                            loader: 'image - webpack - loader',
                            options: {
                                mozjpeg: {
                                    progressive: true,
                                    quality: 65
                                },
                                // optipng.enabled: false will disable optipng
                                optipng: {
                                    enabled: false
                                },
                                pngquant: {
                                    quality: [0.65, 0.90],
                                    speed: 4
                                },
                                gifsicle: {
                                    interlaced: false
                                },
                                // the webp option will enable WEBP
                                webp: {
                                    quality: 75
                                }
                            }
                        }
                    ]
                }
            ]
        }
    };
    
  4. 缓存策略:设置合理的缓存策略可以提高应用的加载速度。在 Webpack 配置中,可以通过设置输出文件的哈希值来实现缓存控制。Angular CLI 默认会在输出文件名中添加哈希值。例如,output.filename 可以设置为 [name].[contenthash].js,这样当文件内容发生变化时,哈希值也会改变,浏览器会重新下载新的文件,而对于未改变的文件,浏览器可以从缓存中加载。
    module.exports = {
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: '[name].[contenthash].js'
        }
    };
    

常见问题及解决方法

  1. 构建速度慢:如果构建速度慢,可能是因为依赖过多或者配置不当。可以通过以下方法解决:
    • 分析依赖关系:使用 webpack - bundle - analyzer 插件来分析项目的依赖关系,找出体积较大的模块并进行优化。首先安装该插件:
      npm install webpack - bundle - analyzer --save - dev
      
      然后在 webpack.extra.js 中添加如下配置:
      const BundleAnalyzerPlugin = require('webpack - bundle - analyzer').BundleAnalyzerPlugin;
      
      module.exports = {
          plugins: [
              new BundleAnalyzerPlugin()
          ]
      };
      
      执行构建时,会弹出一个可视化界面,显示各个模块的大小和依赖关系,帮助你找出可优化的部分。
    • 优化加载器配置:确保加载器的配置是最优的。例如,对于 babel - loader,可以设置 cacheDirectory 选项来启用缓存,提高编译速度。
      {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
              loader: 'babel - loader',
              options: {
                  cacheDirectory: true
              }
          }
      }
      
  2. 样式加载问题:有时可能会遇到样式加载不正确的情况。这可能是由于加载器配置错误或者样式文件路径问题。
    • 检查加载器配置:确保 css - loaderstyle - loader 等相关加载器的配置正确。例如,对于 SCSS 文件,需要安装并配置 sass - loadernode - sass 等加载器。
      npm install sass - loader node - sass --save - dev
      
      然后在 webpack.extra.js 中添加如下配置:
      module.exports = {
          module: {
              rules: [
                  {
                      test: /\.scss$/,
                      use: [
                          'style - loader',
                          'css - loader',
                          'sass - loader'
                      ]
                  }
              ]
          }
      };
      
    • 检查样式文件路径:确保在组件中引用样式文件的路径是正确的。在 Angular 组件中,可以使用相对路径引用样式文件,例如:
      @Component({
          selector: 'app - component',
          templateUrl: './app.component.html',
          styleUrls: ['./app.component.scss']
      })
      export class AppComponent {}
      
  3. TypeScript 编译错误:在 Angular 项目中,TypeScript 编译错误比较常见。这可能是由于 TypeScript 版本不兼容、配置错误或者代码本身存在问题。
    • 检查 TypeScript 版本:确保项目中的 TypeScript 版本与 Angular 版本兼容。可以在 package.json 文件中查看 @angular/coretypescript 的版本,并参考官方文档确认兼容性。
    • 检查 tsconfig.json 配置tsconfig.json 文件包含了 TypeScript 的编译配置。确保配置正确,例如 targetmodulestrict 等选项。如果项目中使用了装饰器,需要确保 experimentalDecoratorsemitDecoratorMetadata 选项设置为 true
    • 检查代码错误:仔细检查报错信息,定位代码中的问题。常见的问题包括类型不匹配、未定义变量等。例如,如果在组件中使用了未定义的变量,TypeScript 编译器会报错。

通过深入理解 Webpack 与 Angular 框架的构建流程,我们能够更好地优化项目,提高应用的性能和开发效率。在实际开发中,不断地探索和调整配置,以适应项目的需求是非常重要的。同时,关注 Webpack 和 Angular 的官方文档和社区,及时了解最新的特性和优化技巧,也是保持项目竞争力的关键。