webpack是什么?

发布时间 2023-07-04 20:26:24作者: unuliha

一、webpack是什么? [2]

Webpack是一个开源的JavaScript模块打包工具,其最核心的功能是解决模块之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并为一个JS文件(有时会有多个,这里讨论的只是最基本的情况)。这个过程就叫作模块打包。
你可以把Webpack理解为一个模块处理工厂。我们把源代码交给Webpack,由它去进行加工、拼装处理,产出最终的资源文件,等待送往用户。

1.1 为什么不直接将工程中的html、CSS、JS等源文件发布到服务器或CDN,而要交给Webpack处理呢?

开发一个简单的Web应用,其实只需要浏览器和一个简单的编辑器就可以了。但是当应用的规模大了之后,就必须借助一定的工具,否则人工维护代码的成本将逐渐变得难以承受。此时就需要按照特定的功能将代码拆分为多个代码段,每个代码段实现一个特定的目的。你可以对其进行独立的设计、开发和测试,最终通过接口来将它们组合在一起。这就是基本的模块化思想。

如果把程序比作一个城市,这个城市内部有不同的职能部门,如学校、医院、消防局等。程序中的模块就像这些职能部门一样,每一个都有其特定的功能。各个模块协同工作,才能保证程序的正常运转。

1.2 JavaScript的模块化如何实现呢?

在大多数程序语言(如C、C++、Java)中,开发者都可以直接使用模块进行开发。工程中的各个模块在经过编译、链接等过程后会被整合成单一的可执行文件并交由系统运行。

对于JavaScript来说,情况则有所不同。在过去的很长一段时间里,JavaScript这门语言并没有模块这一概念。如果工程中有多个JS文件,我们只能通过script标签将它们一个个插入页面中。

<body>
    <script src="c.js"></script>
    <script src="b.js"></script>
    <script src="a.js"></script>
</body>

为何偏偏JavaScript没有模块呢?如果要追溯历史原因,JavaScript之父Brendan Eich最初设计这门语言时只是将它定位成一个小型的脚本语言,用来实现网页上一些简单的动态特性,远没有考虑到会用它实现今天这样复杂的场景,模块化当然也就显得多余了。
随着技术的发展,JavaScript已经不仅仅用来实现简单的表单提交等功能,引入多个script文件到页面中逐渐成为一种常态,但我们发现这种做法有很多缺点。

  • 需要手动维护JavaScript的加载顺序。页面的多个script之间通常会有依赖关系,但由于这种依赖关系是隐式的,除了添加注释以外很难清晰地指明谁依赖了谁,所以当页面中加载的文件过多时很容易出现问题。
  • 每一个script标签都意味着需要向服务器请求一次静态资源,在HTTP 2还没有出现的时期,建立连接的成本是很高的,过多的请求会严重拖慢网页的渲染速度。
  • 在每个script标签中,顶层作用域即全局作用域,没有任何处理而直接在代码中进行变量或函数声明会污染全局作用域。

模块化则解决了上述所有问题。

  • 通过导入和导出语句我们可以清晰地看到模块间的依赖关系,这点在后面会做详细的介绍。
  • 模块可以借助工具来进行打包,所以在页面中只需要加载合并后的资源文件,减少了网络开销。
  • 多个模块之间的作用域是隔离的,彼此不会有命名冲突。

从2009年开始,JavaScript社区开始对模块化进行不断的尝试,并先后给出了AMD、CommonJS、CMD等解决方案。但这些都只是由社区提出的,并不能算作语言本身的特性。而在2015年,ECMAScript 6.0(ES6)正式定义了JavaScript模块标准,使这门语言在诞生了20年之后终于拥有了模块这一概念。

ES6模块标准目前已经得到了大多数现代浏览器的支持,但在实际应用方面还需要等待一段时间,主要有以下几点原因。

  • 无法使用代码分片(code splitting)和删除死代码(tree shaking)
  • 大多数npm模块还是CommonJS的形式,而浏览器并不支持其语法,因此这些包没有办法直接拿来用。
  • 仍然需要考虑个别浏览器及平台的兼容性问题。

那么,如何才能让我们的工程在使用模块化的同时也能正常运行在浏览器中呢?这就到了模块打包工具出场的时候了。

1.2.3 模块打包工具

模块打包工具(module bundler)的任务就是解决模块间的依赖,使其打包后的结果能运行在浏览器上。它的工作方式主要分为两种。

  • 将存在依赖关系的模块按照特定规则合并为单个JS文件,一次全部加载进页面中。
  • 在页面初始时加载一个入口模块,异步加载其他模块。

目前社区中比较流行的模块打包工具有Webpack、Vite、Parcel、Rollup等。

1.2.4 为什么选择Webpack?

对比同类模块打包工具,Webpack具备以下几点优势。

  • Webpack默认支持多种模块标准,包括AMD、CommonJS以及最新的ES6模块,而其他工具大多只能支持一到两种。Webpack对于一些同时使用多种模块标准的工程非常有用,它会帮我们处理好不同类型模块之间的依赖关系。
  • Webpack有完备的代码分片解决方案。从字面意思去理解,它可以分割打包后的资源,在首屏只加载必要的部分,将不太重要的功能放到后面动态加载。这对于资源体积较大的应用来说尤为重要,可以有效地减小资源体积,提升首页渲染速度。
  • Webpack可以处理各种类型的资源。除了JavaScript以外,Webpack还可以处理样式、模板,甚至图片等,而开发者需要做的仅仅是导入它们。比如你可以从JavaScript文件导入一个CSS或者PNG,而这一切最终都可以由第4章讲到的loader来处理。
  • Webpack拥有庞大的社区支持。除了Webpack核心库以外,还有无数开发者来为它编写周边插件和工具。对于绝大多数的需求,你都可以直接在社区找到已有解决方案,甚至会找到多个解决方案。

二、webpack的基本用法? [2]

2.1 安装

  • 安装LTS(Long Term Support,长期维护)版本的Node.js
  • 首先用npm init创建一个项目,我们会看到目录中生成了一个package.json文件,它相当于npm项目的说明书,里面记录了项目名称、版本、仓库地址等信息。
  • 安装webpack和webpack-cli(注意区分本地安装和全局安装,这里二章节的内容使用本地安装)
npm install webpack webpack-cli -D

安装指令中的-D参数是将webpack和webpack-cli作为工程的devDependencies(开发环境依赖)记录在package.json中。这样做是因为webpack仅仅在本地开发时才会用到,在生产环境中并不需要它,所以放在devDependencies中是比较恰当的。假如工程上线时要进行依赖安装,就可以通过npm install --only=prod过滤掉devDependencies中的冗余模块,从而加快安装和发布的速度。
与之相对应的,-S即包名会被注册在package.json的dependencies里面,在生产环境下这个包的依赖依然存在。

2.2 打包第一个应用

先在工程目录下添加以下几个文件

index.js:

import addContent from './add-content.js';
document.write('My first Webpack app.<br />');
addContent();

add-content.js:

export default function() {
    document.write('Hello world!');
}

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>My first Webpack app.</title>
</head>
<body>
    <script src="./main.js"></script>
</body>
</html>

然后在控制台输入打包命令:

npx webpack --entry=./index.js --mode=development

打包完成后会自动生成dist目录,目录下会有一个main.js文件,这时将main.js放到index.html同级目录下(为了保证代码中的<script src="./main.js"></script>能访问到main.js,博主个人注,可以用express启动webpack打包好后的本地文件),用浏览器打开index.html,就可以看到结果了。
image.png

图片说明

现在回过头来看下webpack打包的参数:

  • entry,资源打包的入口。Webpack从这里开始进行模块依赖的查找,找到index.js和add-content.js两个模块,并通过它们来生成最终产物。
  • output-filename, 输出资源名。你会发现打包完成后工程中出现了一个dist目录,其中包含的main.js就是Webpack的打包结果。
  • mode, 打包模式。Webpack为开发者提供了development、production、none三种模式。当置于development和production模式下时,它会自动添加适合当前模式的一系列配置,减少了人为的工作量。在开发环境下,一般设置为development模式就可以了。

2.3 使用npm scripts

从上面的例子不难发现,我们每进行一次打包都要输入一段冗长的命令,这样做不仅耗时,而且容易出错。为了使命令行指令更简洁,可以在package.json中添加一个脚本命令。编辑工程中的package.json文件:

……
  "scripts": {
    "build": "webpack --entry=./index.js --mode=development"
  },
……

scripts是npm提供的脚本命令功能,在这里我们可以直接使用由模块添加的指令(比如用webpack取代之前的npx webpack)。
重新打包,这次输入npm命令即可:

npm run build

2.4 使用默认目录配置

上面的index.js是放在工程根目录下的,而通常情况下我们会分别设置源码目录与资源输出目录。工程源代码放在/src中,输出资源放在/dist中。

在工程中创建一个src目录,并将index.js和add-content.js移动到该目录下。对于资源输出目录来说,Webpack已经默认是/dist,我们不需要做任何改动.

另外需要提到的是,Webpack默认的源代码入口就是src/index.js(因此,要想省略entry参数,需要创建一个src目录,并将index.js及其他需要打包的文件放入其中),因此现在可以省略entry的配置了。编辑package.json:

……
  "scripts": {
    "build": "webpack --output-filename=bundle.js --mode=development"
  },
……

image.png

2.5 使用配置文件

为了满足不同应用场景的需求,Webpack拥有非常多的配置项以及相对应的命令行参数。我们可以通过Webpack的帮助命令来进行查看。

npx webpack –h

图片说明
从之前我们在package.json中添加的脚本命令来看,当项目需要越来越多的配置时,就要往命令中添加更多的参数,那么到后期维护起来就会相当困难。为了解决这个问题,可以把这些参数改为对象的形式专门放在一个配置文件里,在Webpack每次打包的时候读取该配置文件即可。
Webpack的默认配置文件为webpack.config.js(也可以使用其他文件名,需要使用命令行参数指定)。现在让我们在工程根目录(src的同级目录)下创建webpack.config.js,并添加如下代码:

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
    },
    mode: 'development',
}

上面通过module.exports导出了一个对象,也就是打包时被Webpack接收的配置对象。先前在命令行中输入的一大串参数就都要改为key-value的形式放在这个对象中。
之前的参数--output-filename和--output-path现在都成为output下面的属性。filename和先前一样都是bundle.js,不需要改动,而path和之前有所区别。Webpack对于output.path的要求是使用绝对路径(从系统根目录开始的完整路径),之前我们在命令行中为了简洁而使用了相对路径。而在webpack.config.js中,我们通过调用Node.js的路径拼装函数path.join,将__dirname(Node.js内置全局变量,值为当前文件所在的绝对路径)与dist(输出目录)连接起来,得到了最终的资源输出路径。
现在我们可以去掉package.json中配置的打包参数了:

……
  "scripts": {
    "build": "webpack"
  },
……

2.6 webpack-dev-server

到这里,其实我们已经把Webpack的初始环境配置好了。你可能会发现,单纯使用Webpack以及它的命令行工具来进行开发调试的效率并不高。以往只要编辑项目源文件(JS、CSS、HTML等),刷新页面即可看到效果,现在多了一步打包,即我们在改完项目源码后要执行npm run build更新bundle.js,然后才能刷新页面生效。有没有更简便的方法呢?
其实Webpack社区已经为我们提供了一个便捷的本地开发工具——webpack-dev-server。用以下命令进行安装:

npm install webpack-dev-server -D

为了便捷地启动webpack-dev-server,我们在package.json中添加一个dev指令:

……
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  },
……

最后,我们还需要对webpack-dev-server进行配置。编辑webpack.config.js:

module.exports = {
    entry: './src/index.js',
    output: {
        filename: './main.js',
    },
    mode: 'develpoment',
	// dev-server默认会访问 public/index.html
    devServer: {},
};

可以看到,我们在配置中添加了一个devServer对象,它是专门用来放webpack-dev-server配置的。webpack-dev-server可以看作一个服务者,它的主要工作就是接收浏览器的请求,然后将资源返回。 当服务启动时,它会先让Webpack进行模块打包并将资源准备好(在示例中就是bundle.js)。当webpack-dev-server接收到浏览器的资源请求时,它会首先进行URL地址校验。如果该地址是资源服务地址(上面配置的publicPath),webpack-dev-server就会从Webpack的打包结果中寻找该资源并返回给浏览器。反之,如果请求地址不属于资源服务地址,则直接读取硬盘中的源文件并将其返回。
综上我们可以总结出webpack-dev-server的两大职能。

  • 令Webpack进行模块打包,并处理打包结果的资源请求。
  • 作为普通的Web Server,处理静态资源文件请求。
    图片说明

这里有一点需要注意。直接用Webpack开发和使用webpack-dev-server有一个很大的区别,前者每次都会生成main.js,而webpack-dev-server只是将打包结果放在内存中,并不会写入实际的bundle.js,在每次webpack-dev-server接收到请求时都只是将内存中的打包结果返回给浏览器。
webpack-dev-server还有一项很便捷的特性——live-reloading(自动刷新)。例如我们保持本地服务启动以及浏览器打开的状态,到编辑器中去更改add-content.js:

export default function() {
    document.write('This is from live-reloading!');
}

此时切回到浏览器,你会发现浏览器的内容自动更新了,这就是live-reloading的功能。当webpack-dev-server发现工程源文件进行了更新操作后就会自动刷新浏览器,显示更新后的内容。该特性可以提升本地开发的效率。

三、webpack的loader(预处理器)?[1][2][3]

我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括 一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。对于webpack本身的能力来说,对于这些转化是不支持的。我们需要给webpack扩展对应的loader就可以了。
本质是一个函数,对接收到的内容进行转换,返回转换后的结果。可以通过 loader 的预处理函数,为 js生态系统提供更多能力。

3.1 概述

loader是Webpack中的一个核心概念,我们可以将其理解为一个代码转换的工具。每个loader本质上都是一个函数,可以表示为以下形式:

output = loader(input)

这里的input可能是工程源文件的字符串,也可能是上一个loader转化后的结果,output则包括了转化后的代码、source-map和AST对象。如果这是最后一个loader,结果将直接被送到Webpack进行后续处理,否则将作为下一个loader的输入向后传递。
loader可以是链式的。我们可以对一种资源设置多个loader,第一个loader的输入是文件源码,之后所有loader的输入都为上一个loader的输出。如在工程中编译SCSS时,我们可能需要如下loader:

Style标签 = style-loader(css-loader(sass-loader(SCSS)))

loader本身就是一个函数,该函数会对接收到的内容进行转换,然后返回转换后的结果(可能包含source-map和AST对象)。

3.2 loader的配置

Webpack本身只认识JavaScript,对于其他类型的资源,我们必须预先定义一个或多个loader对其进行转译,输出为Webpack能够接收的形式再继续进行,因此loader做的实际上是一个预处理的工作。

3.2.1 loader的引入

假设我们要处理CSS,首先依照Webpack“一切皆模块”的思想,从一个JS文件加载一个CSS文件。如:

// app.js
import './style.css';
   
// style.css
body {
    text-align: center;
    padding: 100px;
    color: #fff;
    background-color: #09c;
}

此时工程中还没有任何loader,如果直接打包会看到报错提示,如下图所示。
图片说明

Webpack无法处理CSS语法,因此抛出了一个错误,并提示需要使用一个合适的loader来处理这种文件。
下面我们把css-loader加到工程中。loader都是一些第三方npm模块,Webpack本身并不包含任何loader,因此使用loader的第一步就是先从npm安装它。在工程目录下执行命令:

npm install css-loader -D

接下来我们将loader引入工程中,具体配置如下:

module.exports = {
    // ...
    module: {
        rules: [{
            test: /\.css$/,
            use: ['css-loader'],
        }],
    },
};

与loader相关的配置都在module对象中,其中module.rules代表了模块的处理规则。每条规则内部可以包含很多配置项,这里我们只使用了最重要的两项——test和use。

  • test可接收一个正则表达式或者一个元素为正则表达式的数组,只有正则匹配上的模块才会使用这条规则。在本例中以/.css$/来匹配所有以.css结尾的文件。
  • use可接收一个数组,数组包含该规则所使用的loader。在本例中我们只配置了一个css-loader,在只有一个loader时也可以将其简化为字符串“css-loader”。

此时我们再进行打包,之前的错误应该已经消失了,但是CSS的样式仍然没有在页面上生效。这是因为css-loader的作用仅仅是处理CSS的各种加载语法(@import和url()函数等),如果要使样式起作用还需要style-loader来把样式插入页面。css-loader与style-loader通常是配合在一起使用的。

3.2.2 链式loader

很多时候,在处理某一类资源时我们都需要使用多个loader。如,对于SCSS类型的资源来说,我们需要sass-loader来处理其语法,并将其编译为CSS;接着再用css-loader处理CSS的各类加载语法;最后使用style-loader来将样式字符串包装成style标签插入页面。
下面引入style-loader,首先还是使用npm安装。

npm install style-loader  -D

接着之前的配置,更改rules中的规则。

 module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader'],
            }
        ],
    },
};

我们把style-loader加到了css-loader前面,这是因为Webpack在打包时是按照数组从后往前的顺序将资源交给loader处理的,因此要把最后生效的放在前面
此时再进行打包,样式就会生效了。页面中会插入一个style标签,包含CSS文件的样式。至此,我们就完成了从JS文件加载CSS文件的配置。

3.2.3 loader 更多配置

loader作为预处理器通常会给开发者提供一些配置项,在引入loader的时候可以通过options将它们传入。如:

rules: [
    {
        test: /\.css$/,
        use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    // css-loader 配置项
                },
            }
        ],
    },
],

有些loader可能会使用query来代替options,从功能来说它们并没有太大的区别,具体如何使用需参阅loader的文档。
此外,还有其他相关配置。

  • exclude与include
    exclude与include用于排除或包含指定目录下的模块,可接收正则表达式或者字符串(文件绝对路径),或者由它们组成的数组。
rules: [
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /node_modules/,
    }
],

上面exclude的含义是,所有被正则匹配到的模块都排除在该规则之外,也就是说,node_modules中的模块不会执行这条规则。该配置项通常是必加的,否则将拖慢整体的打包速度。
举个例子,在项目中我们经常会使用babel-loader来处理ES6+语言特性,但是对于node_modules中的JS文件来说,绝大多数都是已经编译为ES5的,因此没有必要再使用babel-loader来进行额外处理。
除exclude外,使用include配置也可以达到类似的效果。请看下面的例子:

rules: [
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        include: /src/,
    }
],

include代表该规则只对正则匹配到的模块生效。假如我们将include设置为工程的源码目录,自然而然就将node_modules等目录排除掉了。
当exclude和include同时存在时,exclude的优先级更高。请看下面的例子:

rules: [
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /node_modules/,
        include: /node_modules\/awesome-ui/,
    }
],

此时,node_modules已经被排除了,但是假如我们想让该规则对node_modules中的某一个模块生效,即便加上include也是无法覆盖exclude配置的。要实现这样的需求,我们可以更改exclude中的正则表达式。

rules: [
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        // 排除node_modules中除了foo和bar以外的所有模块
        exclude: /node_modules\/(?!(foo|bar)\/).*/,
    }
],

另外,由于exclude优先级更高,我们可以对include中的子目录进行排除。请看下面的例子:

rules: [
    {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /src\/lib/,
        include: /src/,
    }
],

通过include,我们将该规则配置为仅对src目录生效,但是仍然可以通过exclude排除其中的src/lib目录。

3.3 常用loader介绍

3.3.1 babel-loader

babel-loader用于处理ES6+并将其编译为ES5,它使我们能够在工程中使用最新的语言特性(甚至还在提案中),同时不必特别关注这些特性在不同平台的兼容问题。
安装:

npm install babel-loader @babel/core @babel/preset-env -D
  • babel-loader:它是使Babel与Webpack协同工作的模块。
  • @babel/core:顾名思义,它是Babel编译器的核心模块。
  • @babel/preset-env:它是Babel官方推荐的预置器,可根据用户设置的目标环境自动添加所需的插件和补丁来编译ES6+代码。
rules: [
  {
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        cacheDirectory: true,
        presets: [[
          'env', {
            modules: false,
          }
        ]],
      },
    },
  }
],
  • 1)由于该规则中的babel-loader会对所有JS后缀文件生效,所以我们需要特别排除掉node_modules目录,否则会令babel-loader编译其中所有的模块,严重拖慢打包的速度,甚至改变第三方模块的原有行为。
  • 2)对于babel-loader本身我们添加了cacheDirectory配置项,它会启用缓存机制,在重复打包未改变过的模块时防止二次编译,加快打包的速度。cacheDirectory可以接收一个字符串类型的路径来作为缓存路径,这个值也可以为true,此时其缓存目录会指向node_modules/.cache/babel-loader。
  • 3)由于@babel/preset-env会将ES6 Module转化为CommonJS的形式,这会导致Webpack中的tree-shaking特性失效。将@babel/preset-env的modules配置项设置为false会禁用模块语句的转化,而将ES6 Module的语法交给Webpack本身处理。

babel-loader支持从.babelrc文件读取Babel配置,因此可以将presets和plugins从Webpack配置文件中提取出来,效果是相同的。

3.3.2 ts-loader

ts-loader与babel-loader的性质类似,它是用于连接Webpack与TypeScript的模块。通过TypeScript和ts-loader,我们可以实现代码类型检查。
可使用以下命令进行安装:

npm install ts-loader typescript -D
rules: [
    {
        test: /\.ts$/,
        use: 'ts-loader',
    }
],

需要注意的是,TypeScript本身的配置并不在ts-loader中,而是必须放在工程目录下的tsconfig.json中。如:

{
    "compilerOptions": {
        "target": "es5",
        "sourceMap": true,
    },
},

3.3.3 html-loader

html-loader用于将HTML文件转化为字符串并进行格式化,使得我们可以把一个HTML片段通过JS加载进来。

npm install html-loader -D
rules: [
    {
        test: /\.html$/,
        use: 'html-loader',
    }
],

使用示例如下:

// header.html
<header>
    <h1>This is a Header.</h1>
</header>
   
// index.js
import headerHtml from './header.html';
document.write(headerHtml);

header.html将会转化为字符串,并通过document.write插入页面中。

3.3.4 file-loader

file-loader用于打包文件类型的资源,并返回其publicPath。

npm install file-loader -D

请看下面的例子:

const path = require('path');
module.exports = {
    entry: './app.js',
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: './assets/',
    },
    module: {
        rules: [
            {
                test: /\.(png|jpg|gif)$/,
                use: 'file-loader',
            }
        ],
    },
};

此时图片路径会成为如下形式:

import avatarImage from './avatar.jpg';
console.log(avatarImage); // ./assets/c6f482ac9a1905e1d7d22caa909371fc.jpg

注意,Webpack 5也提供了另一种方式(asset/resource)来处理文件类型资源,可以用来替代file-loader。并且这种方式是内置的,使用起来更加便捷:

rules: [
    {
        test: /\.(png|jpg|gif)$/,
        type: 'asset/resource'
    }
]

3.3.5 url-loader

url-loader的作用与file-loader类似,唯一的不同在于,url-loader允许用户设置一个文件大小的阈值,当大于该阈值时它会与file-loader一样返回publicPath,而小于该阈值时则返回base64形式的编码。

npm install url-loader -D

Webpack配置如下:

rules: [
    {
        test: /\.(png|jpg|gif)$/,
        use: {
            loader: 'url-loader',
            options: {
                limit: 10240,
                name: '[name].[ext]',
                publicPath: './assets-path/',
            },
        },
    }
],

url-loader可以接收与file-loader相同的参数,如name和publicPath等,也可以接收一个limit参数。使用示例如下:

import avatarImage from './avatar.jpg';
console.log(avatarImage); // data:image/jpeg;base64,/9j/2wCEAAgGBg……

由于图片大小在limit阈值以下,因此经过url-loader转化后得到的是base64形式的编码。
注意,Webpack 5也提供了内置的解决方案,可以替代url-loader来处理inline类型的资源。

rules: [
    {
        test: /\.svg$/,
        type: 'asset/inline'
    }
]

四、@angular-builders/custom-webpack

允许在不弹窗进行webpack配置的情况下,自定义webpack的构建配置。

4.1 @angular-builders/custom-webpack:browser

扩展了的@angular-builders/custom-webpack:browser构建器,允许在现有封装好的配置上增加额外的webpack配置,以及index.html转换。本构建器会执行和@angular-devkit/build-angular:browser相同的构建动作,只是会增加指定的webpack配置。同样地,也会执行指定的index.html转换。

4.2.1 官方例子:

"architect": {
  ...
  "build": {
    "builder": "@angular-builders/custom-webpack:browser",
    "options": {
      "customWebpackConfig": {
        "path": "./extra-webpack.config.js",
        "mergeRules": {
          "externals": "replace"
        }
      },
      "indexTransform": "./index-html-transform.js",
      "outputPath": "dist/my-cool-client",
      "index": "src/index.html",
      "main": "src/main.ts",
      "polyfills": "src/polyfills.ts",
      "tsConfig": "src/tsconfig.app.json"
    }

在这个例子中,extra-webpack.config.js中的externals条目,将会替换Angular CLI底层webpack配置中的externals条目。其余的参数都将被附加。此外,index.html将被./index-html-transform.js导出的函数修改。
构建器参数:

  • 所有@angular-devkit/build-angular:browser的参数配置。
  • customWebpackConfigsee below
  • indexTransform:see below

参考

  1. webpack的Loaders
  2. Webpack实战:入门、进阶与调优
  3. webpack中的loader
  4. Custom webpack builders for Angular build facade
  5. CommonJS or AMD dependencies can cause optimization bailouts的解决办法
  6. angular.json参数配置详解