本文主要记录webpack5的基础学习。最终代码放在文章末尾github上

什么是Webpack

一个打包编译静态资源的工具,可以把前端的静态资源进行打包压缩,有利于用户访问,减少带宽的占用

Webpack五大概念

官方Webpack五个概念:https://webpack.docschina.org/concepts/

入口(entry)

官网介绍:入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

默认值是 ./src/index.js,但你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js',
};

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的起点。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};

output属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。

loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。

在更高层面,在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义出在进行转换时,应该使用哪个 loader。

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');

module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
],
},
};

webpack 只能理解 JavaScript 和 JSON 文件。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。

插件(plugin)

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

webpack.config.js

在这个示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。

1
2
3
4
5
6
7
8
9
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
module: {
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

loader 只能用于转换某些类型的模块,而插件则可以用于处理其他一些模块。包括:打包优化,资源管理,注入环境变量。

模式(mode)

通过选择 development, productionnone 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production

1
2
3
module.exports = {
mode: 'production',
};

webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要提前加载 polyfill。

1
npm install --save babel-polyfill
1
2
3
4
5
6
7
8
9
10
import 'babel-polyfill';

function component() {
var element = document.createElement('div');
element.innerHtml = join(['Hello', 'webpack'], '');
return element;
}

document.body.appendChild(component());

如何使用

学习一门新的技术,应该最先最小化测试运行起来一个项目:

最小化安装使用

初始化项目:

1
2
3
4
# 初始化
npm init -y
# 安装webpack和cli
npm install webpack webpack-cli -D

因为webpack默认入口是src下的indexjs,所以我们根目录创建src,在src目录下创建index.js。

src/index.js

1
console.log('hello webpack!')

这时候我们直接运行:

1
npx webpack

这时候webpack就会以刚才的src/index.js文件为入口进行打包,打包后的文件会默认存放到dist目录下,我们也可以通过修改package.json文件的构建命令:

1
2
3
"scripts": {
"bulid": "webpack"
},

这样我们就可以直接构建了。

完善配置

显然刚才只是一个极小的运行环境测试,webpack最主要还是配置文件,接下来我们在根目录创建webpack.config.js配置文件。(注意名称这里,默认是webpack.config.js,如果是需要改其他名,需要修改构建命令,具体参考https://webpack.docschina.org/configuration/)

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
const path = require('path');

module.exports = {
mode: 'development',//开发模式
entry: './src/index.js',//入口文件
output: {
path: path.resolve(__dirname, 'dist'),//输出的路径,需要绝对路径,
filename: 'foo.bundle.js',//输出的名字
},
};

使用npm run bulid既可以构建出文件。

如果出于某些原因,需要根据特定情况使用不同的配置文件,则可以通过在命令行中使用 --config 标志修改。

package.json

1
2
3
"scripts": {
"build": "webpack --config prod.config.js"
}

经过上面的完善,我们配置了wecpack的配置文件,并且指明了开发环境,输入和输出,接下来继续完善

配置loader

JS是不认识CSS、图片或者其他的静态文件的,这时候我们就需要使用loader让JS来处理这些文件。

webpack4有三种,但是webpack只有两种方式,但是这里只列出官方推荐的配置方式:(具体可以参考:https://webpack.docschina.org/concepts/loaders)

我们处理CSS样式,这里需要下载:

1
npm install style-loader css-loader -D

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const path = require('path');

module.exports = {
mode: 'development', //开发模式
entry: './src/index.js', //入口文件
output: {
path: path.resolve(__dirname, 'dist'), //输出的路径
filename: 'foo.bundle.js', //输出的名字
},
// rules是module对象里面的
module: {
rules: [
{
test: /\.css$/, //处理后缀为CSS的文件
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
],
},
};

rules是一个数组,可以配置多个loader,loader对象里面有test和use属性,test是用来指出需要处理哪些文件的,use是指出用哪个loader处理。一个文件可以由多个loader处理。

接下来我们来解释一下module下rules的每个部分的作用:

  1. test: /\.css$/: 这里定义了一个正则表达式,用于匹配所有以 .css 结尾的文件,表示这些文件需要被这个 loader 处理。
  2. use: 是一个 loader 数组,定义了处理 .css 文件的一系列 loader。
    • style-loader: 这个 loader 将 CSS 插入到 DOM 中的 <style> 标签中,使得 CSS 样式生效。
    • css-loader: 这个 loader 负责加载 CSS 文件,并且解析 CSS 文件之间的关系,比如 @importurl() 引用等。通过设置 modules: true,启用了 CSS 模块化,使得每个 CSS 类名都局部作用域化,避免全局污染和命名冲突。

综合起来,这段配置的作用是:当 webpack 在打包过程中遇到以 .css 结尾的文件时,先用 css-loader 处理,然后用 style-loader 将处理后的样式通过 <style> 标签插入到页面中,同时实现了 CSS 的模块化管理。

需要注意的是,rules是有执行的顺序的,执行的顺序是书写顺序的倒序。

css-loader的 modules: true的设置:

不设置 modules: true 不会导致错误,但可能会影响你的 CSS 样式管理方式。两种设置的区别:

  1. modules: true 的情况
    • modules: true 时,css-loader 会将每个 CSS 类名编译成局部作用域的形式,通常是通过生成唯一的类名来实现。这样做的好处是,可以避免全局 CSS 污染和类名冲突,特别是在大型项目中使用时,非常有用。
  2. **不设置 modules: true**:
    • 如果没有设置 modules: true,则 css-loader 不会进行类名的局部作用域化处理,而是保留原始的 CSS 类名,直接应用到页面中。这种情况下,所有的 CSS 类名都是全局有效的,可能会导致命名冲突和不可预测的样式覆盖问题,特别是在复杂的应用中。

因此,是否设置 modules: true 取决于你的项目需求和管理风格。对于大多数现代项目,特别是使用模块化开发的情况下,推荐设置 modules: true 来保持 CSS 的模块化管理,提高代码的可维护性和安全性。

配置插件(plugin)

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

下载 html-webpack-plugin

1
npm install html-webpack-plugin -D

示例中,html-webpack-plugin 为应用程序生成一个 HTML 文件,名为indexdist.html,并自动将生成的所有 bundle 注入到此文件中。template是将已经好的文件也打包过去

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development', //开发模式
entry: './src/index.js', //入口文件
output: {
path: path.resolve(__dirname, 'dist'), //输出的路径
filename: 'foo.bundle.js', //输出的名字
},
// rules是module对象里面的
module: {
rules: [
{
test: /\.css$/, //处理后缀为CSS的文件
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: 'indexdist.html',
template: 'index.html',
}),
],
};

提醒:Webpack的配置对象中,plugins 应该是直接放在配置对象的顶层,而不是放在 module 下面。

配置模式(mode)

通过选择 development, productionnone 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production

1
2
3
module.exports = {
mode: 'development',
};

支持以下字符串值:

选项 描述
development 会将 DefinePluginprocess.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。
production 会将 DefinePluginprocess.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,
none 不使用任何默认优化选项

配置热更新热加载

每次都需要bulid非常麻烦,webpack提供了一个watch命令,可以帮助我们热编译,

1
2
3
4
"scripts": {
"bulid": "webpack --watch",
"bulid:watch": "webpack --watch"
},

但是,这个办法,还是每次都需要刷新才可以看到效果,这就需要webpack的模块热替换(hot module replacement)具体参考:https://webpack.docschina.org/concepts/hot-module-replacement/

根据官方的文档:

在开发环境,可以将 HMR 作为 LiveReload 的替代。webpack-dev-server 支持 hot 模式,在试图重新加载整个页面之前,hot 模式会尝试使用 HMR 来更新。更多细节请查看 模块热替换 指南。

我们要下载webpack-dev-server:

1
npm install webpack-dev-server -D

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development', //开发模式
entry: './src/index.js', //入口文件
devServer: {
hot: true,
static: './dist',
},
output: {
path: path.resolve(__dirname, 'dist'), //输出的路径
filename: 'foo.bundle.js', //输出的名字
clean: true,
},

// rules是module对象里面的
module: {
rules: [
{
test: /\.css$/, //处理后缀为CSS的文件
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
}),
],
};

这个代码中需要把filename: 'index.html',改成index.html.

增加配置:

1
2
3
4
devServer: {
hot: true,
static: './dist',
},

源码

github地址:https://github.com/xiaopalu/webpack_example