什么是Webpack

  1. 官网

  2. 官方解释:webpack is a static module bundler for modern JavaScript applications.

  3. 翻译一下:webpack是一个静态的模块化打包工具,为现代的JavaScript应用程序;

  4. 我们来对上面的解释进行拆解:

    • 打包bundler:webpack可以将帮助我们进行打包,所以它是一个打包工具;
    • 静态的static:这样表述的原因是我们最终可以将代码打包成最终的静态资源(部署到静态服务器);
    • 模块化module:webpack默认支持各种模块化开发,ES Module、CommonJS、AMD等;
    • 现代的modern:现代前端的开发越来越复杂,催生了webpack的出现和发展;
  5. 即:无论开发使用什么语言,最终通过webpack将代码打包成普通的静态资源(详见下文:Webpack依赖图);

环境

Webpack的运行是依赖Node环境的,所以我们电脑上必须有Node环境;

安装

webpack的安装目前分为两个:webpack(代码使用)、webpack-cli(命令行使用);

这里推荐局部安装:假如电脑上有3个项目:vue2,vue3,react;三个项目都需要打包,可能依赖的webpack版本不一样;

所以:应该每个项目有着自己的webpack,需要用什么版本,在本地的局部环境安装对应的webpack版本即可;

1
2
npm install webpack webpack-cli –g # 全局安装(不推荐)
npm install webpack webpack-cli –D # 局部安装(-D开发环境安装,生产环境已经打包成静态资源了,不需要使用webpack)

基础打包

默认打包

  1. 创建个文件夹,使用npm init -y创建package.json;
  2. npm i webpack webpack-cli -D安装局部环境依赖;
  3. 在根目录下新建src文件夹,文件夹下新建入口文件index.js,写入任意js代码;
  4. 在根目录下cmd:通过npx webpack进行打包==>打包成功:根目录下生成dist文件夹,下面有main.js;(可以将此打包之后的js引入html中,可以正常运行);
  5. 但是如果根目录下没有src文件夹(其他名字的文件夹也不行),或者src文件夹下的js不叫index.js进行打包==>打包失败;(即:文件夹必须叫src,入口文件必须叫index.js);
  6. 那如果就不想有src文件夹或index.js如何进行打包呢?这里用到了配置参数;

配置参数

修改文件夹名为location,下面js改为main.js;

  • 指定入口;
1
npx webpack --entry ./location/main.js   #-entry为入口文件的意思, 后面跟 入口文件的路径;

如何修改打包之后的文件夹名和下面的js名呢?

  • 指定输出文件夹名、输出文件名;
1
2
3
npx webpack --entry ./location/main.js --output-filename fsl.js --output-path fsllala
#--output-filename fsl.js 指定输出文件名为 fsl.js
#--output-path fsllala 指定输出文件夹名为 fsllala

但是这样写配置参数,太长了,不方便,如何解决呢?==>单独创建配置文件;

配置文件

在根目录下新建webpack.config.js(固定名字),里面进行参数配置;

此时目录结构如下:

1
2
3
4
5
6
7
8
|—— node_modules
|—— package.json
|—— package-lock.json
|—— webpack.config.js #配置文件
└── location #源码目录 (一般叫src,这里是为了改配置,所以改为了location)
|── main.js #入口文件 (一般叫index.js,这里是为了改配置,所以改为了main.js)
|── name.js #依赖文件
└── age.js #依赖文件
  • webpack.config.js其实是个模块,webpack的运行环境为nodejsnodejs会将该模块导出的内容进行读取;所以需要使用node的CommonJs;不能使用ES6模块,即:
1
2
// webpack.config.js文件
module.exports={} // commonJs
  • 进行相关参数配置
1
2
3
4
5
6
7
8
const path = require("path");
module.exports = {
entry: "./location/main.js", // 入口文件
output: {
filename: "fsl.js", // 输出文件名
path: path.resolve(__dirname, "fsllala"), // 输出文件路径 (这里必须是绝对路径)
},
};
  • 如果不想叫webpack.config.js,而是叫vue.config.js,运行npx webpack==>报错;
1
npx webpack --config vue.config.js # 通过指定配置文件名,可以解决
  • npm run build是怎么做到打包的呢
1
2
3
4
// package.json
"scripts": {
"build":"webpack --config vue.config.js" // 这里不是cmd,所以去掉npx
}

进阶打包

Webpack依赖图

webpack

webpack到底是如何对我们的项目进行打包的呢?

  • 事实上webpack在处理应用程序时,它会根据命令或者配置文件找到入口文件;
  • 从入口开始,会生成一个 依赖关系图,这个依赖关系图会包含应用程序中所需的所有模块(比如.js文件、css文件、图片、字 体等);
  • 然后遍历图结构,打包一个个模块 (webpack默认只给JS文件打包,别的文件需要根据文件的不同使用不同的loader来解析);

loader

新建目录结构如下:

1
2
3
4
5
6
7
8
9
|—— node_modules
|—— package.json
|—— package-lock.json
|—— webpack.config.js #配置文件
|—— index.html
└── src #源码目录
|── index.js #入口文件
|── components # 存放js的文件夹
|── css # 存放css的文件夹

验证loader作用

验证上问提及:webpack默认只给JS文件打包,别的文件需要根据文件的不同使用不同的loader来解析;

期望效果:

  1. index.html 文件中仅引入打包之后的入口文件,别的不做任何操作;
  2. 页面展示有css样式的内容;

编写验证代码

  1. css文件夹下新建div_style.css
1
2
3
4
5
/* css/div_style.css */
.redColor{
color: red;
font-size: 24px;
}
  1. components文件夹下新建div_cpns.js
1
2
3
4
5
6
// components/div_cpns.js
import "../css/div_style.css"; // 模块引入;添加到依赖图中 (css模块没有进行导出)
const div = document.createElement("div");
div.innerHTML = "hello world";
div.classList.add("redColor");
document.body.append(div);
  1. 入口文件引入div_cpns.js
1
2
// src/index.js
import "./components/div_cpns.js"; // 模块引入;添加到依赖图中 (div_cpns.js模块没有进行导出)
  1. 使用npm run build进行打包
1
2
3
4
# package.json 
"scripts": {
"build":"webpack"
},

打包出错,打包结果如下图所示:

css-loader1

上面的错误信息告诉我们需要一个loader来加载这个css文件,但是loader是什么呢?

  • loader 可以用于对模块的源代码进行转换;
  • 我们可以将css文件也看成是一个模块,我们是通过import来加载这个模块的;
  • 在加载这个模块时,webpack其实并不知道如何对其进行加载,我们必须制定对应的loader来完成这个功能;
  • webpack默认支持对js文件的处理;其他文件需要根据文件的不同使用不同的loader来解析;

css-loader与style-loader

  • css-loader:可以解析css文件的loader。但该loader并不会将解析之后的css插入到页面中,即如果只使用该loader会产生css解析完了,但是页面没有效果的现象;
  • style-loader:会将解析之后的css插入到页面,即完成插入style的操作;

loader下载:

1
npm install css-loader style-loader -D  #loader其实都是研发环境的,生产环境都是解析之后的静态文件;

loader配置

  • webpack.config.js文件中的module.rules中允许我们配置多个loader;
  • module.rules的配置如下:
    • rules属性对应的值是一个数组,数组中存放的是一个个的Rule,Rule是一个对象,对象中可以设置多个属性:
      • test属性:用于对 resource(资源)进行匹配的,通常会设置成正则表达式;
      • use属性:对应的值是一个数组,数组里为各种loader;注意:loader的执行顺序是从后往前的;
      • use属性数组内可以为对象,对象属性为:
        • loader:必须有一个 loader属性,对应的值是一个字符串;
        • options:可选的属性,值是一个字符串或者对象,值会被传入到loader中
        • query:目前已经使用options来替代;
      • use属性数组内可以为字符串,是 loader 属性的简写方式;
1
2
3
4
5
6
7
8
9
10
11
12
module.exports={
module:{
rules:[
{
// 告诉webpack匹配什么文件(.在正则中表示除换行符之外的任何字符,所以需要\进行转义)
test:/\.css$/,
// 告诉webpack使用什么loader进行匹配之后的处理(loader的执行顺序是从后往前的)
use:['style-loader','css-loader']
}
]
}
}

使用npm run build进行打包==>打包成功;

index.html引入打包之后的文件,查看效果:

1
<script src="./dist/main.js"></script>

less-loader

less同理,环境不支持该预处理器,需将现将less、sass等编写的css需要通过工具转换成普通的css;

loader下载

1
npm install less-loader -D

配置webpack.config.js

1
2
3
4
5
6
7
8
9
10
module.exports={
module:{
rules:[
{
test:/\.less$/,
use:['style-loader','css-loader','less-loader'] // less会转化为css,所以还需要css-loader
}
]
}
}

PostCSS

参考文献:PostCssPostCSS真的太好用了!

loader下载

1
npm install postcss-loader -D

autoprefixer

  • 有一些css属性存在浏览器兼容问题,需要开发手动添加浏览器前缀;
  • 而Autoprefixer将使用基于当前浏览器支持的特性和属性数据去为你自动添加前缀(-webkit-,-ms-,-moz-)。你可以尝试下Autoprefixer的demo:Autoprefixer CSS online

使用postcss插件Autoprefixer:

  1. 下载
1
npm install autoprefixer -D
  1. 设置单独的postcss配置文件:根目录下,新建postcss.config.js( 注意:因为postcss需要有对应的插件才会起效果,所以我们需要配置它的plugin);
1
2
3
4
// postcss.config.js
module.exports = {
plugins: ["autoprefixer"],
};
  1. 设置loader
1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js
module.exports={
module:{
rules:[
{
test:/\.css$/,
// postcss先对css进行处理,所以写在后面(从后往前解析)
use:['style-loader','css-loader','postcss-loader']
},
]
}
}
  1. 相关css代码
1
2
3
4
5
6
/* css/div_style.css */
.redColor{
color: red;
font-size: 24px;
user-select: none; /* 这个属性有兼容差异,这里我们没做处理, Autoprefixer自动做浏览器兼容处理*/
}
  1. npm run build进行打包,index.html引入打包之后的文件,检查css
  2. 效果如下

Autoprefixer

postcss-preset-env

  • 集成了postcss常用的插件(Autoprefixer,polyfill……);
  • 所以用postcss-preset-env,就不需要安装使用Autoprefixer了,因为内部已经集成了Autoprefixer;

使用postcss插件postcss-preset-env同Autoprefixer步骤;

Webpack打包图片

asset module type

  • 我们当前使用的webpack版本是webpack5:

    • 在webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader;(需要下载对应的loader,然后使用)
    • 在webpack5开始,我们可以直接使用资源模块类型(asset module type),来替代上面的这些loader;(不需要下载loader)
  • **资源模块类型(asset module type)**,通过添加 4 种新的模块类型,来替换所有这些 loader:

    • asset/resource 发送一个单独的文件并导出 URL;(之前通过使用 file-loader 实现)
    • asset/inline 导出一个资源的 data URI;(之前通过使用 url-loader 实现)
    • asset/source 导出资源的源代码(基本不用);(之前通过使用 raw-loader 实现)
    • asset在导出一个 data URI 和发送一个单独的文件之间自动选择;(之前通过使用 url-loader,并且配置资源体积限制实现)

asset module type的使用

  1. 新建文件夹img,用于存放图片;
  2. 在components下,新建img_cpns.js;
1
2
3
4
5
6
7
8
9
// src/components/img_cpns.js

// 在webpack中,任何文件都是一个模块,都可以引入其模块;
// 引入图片模块
import kunkun from "../img/ikun.jpg"; // 因为要对该图片使用,所以起了个别名,而不是直接引入

const img = document.createElement("img");
img.src = kunkun;
document.body.append(img);
  1. 在入口文件里面引入img_cpns.js,构成webpack的依赖图结构;
1
2
3
4
// src/index.js
import "./components/div_cpns.js"; // 模块引入;添加到依赖图中 (div_cpns.js模块没有进行导出)

import "./components/img_cpns.js"; // 模块引入;添加到依赖图中 (img_cpns.js模块没有进行导出)
  1. webpack5版本开始,不需要下载资源loader,只需要配置asset module type即可;

将图片全部转换为一个单独的文件

  • 配置webpack
1
2
3
4
5
6
7
8
9
10
11
12
// webpack.config.js
module.exports={
module:{
rules:[
{
test:/\.(png|jpe?g|gif|svg|webp)$/, // 图片资源 (jpe?g表示jpg和jpeg格式)
// 打包图片,将图片打包成一个单独的文件,然后引入
type:'asset/resource',
}
]
}
}
  • 通过npm run build进行打包;观察打包之后的dist文件夹;**(每次打包前需要删除之前的dist文件夹,防止之前的分包依旧保留,后续可以通过配置实现自动删除)**

发现将原来的ikun.jpg,打包成了1d40d06e317136088c5a.jpg;然后main.js对打包之后的图片地址做了处理;

assetResource

如何做到自定义输出文件名呢?

自定义输出文件名:官网

  • 配置webpack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg|webp)$/, // 图片资源 (jpe?g表示jpg和jpeg格式)
// 打包图片,将图片打包成一个单独的文件,然后引入
type: "asset/resource",
// 匹配符合test条件的文件(如果直接写到output,就不会匹配test条件)
generator: {
filename: "[name]_[hash][ext]",
// [name]代表原文件名;
// [etx]代表原文件后缀;
// [hash]唯一ID(防止打包前不同文件夹内有名字后缀都相同的文件,打包之后无法区分)
},
},
],
},
};

通过npm run build进行打包;观察打包之后的dist文件夹;**(每次打包前需要删除之前的dist文件夹,防止之前的分包依旧保留,后续可以通过配置实现自动删除)**

发现将原来的ikun.jpg,打包成了:ikun_1d40d06e317136088c5a.jpg;

如何将图片资源放到一个单独的文件夹内呢?

  • 配置webpack
1
2
3
generator: {
filename: "images/[name]_[hash][ext]",
},

将图片全部转换为base64格式;

  • 优点:相比较于 asset/resource,少发送(图片加载)网络请求;

  • 缺点:造成JS文件偏大,下载JS文件、解析本身消耗时间非常长==>造成代码阻塞;

  • 配置webpack

1
2
3
4
5
6
7
8
9
10
11
module.exports={
module:{
rules:[
{
test:/\.(png|jpe?g|gif|svg|webp)$/, // 图片资源 (jpe?g表示jpg和jpeg格式)
// 打包图片,inline的寓意就是将图片转为base64,然后放到了打包之后的main.js里面(行内)
type:'asset/inline',
}
]
}
}
  • 通过npm run build进行打包;观察打包之后的dist文件夹;**(每次打包前需要删除之前的dist文件夹,防止之前的分包依旧保留,后续可以通过配置实现自动删除)**

发现dist文件夹里面只有一个main.js;

assetInline

  • 浏览器运行,可看到图片正常显示,图片地址为base64:data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD…P2atuIjKPvHtU5hxbHHVqzLS6xGJv4G6CtISEIPRulecZn//Z

可通过合理的配置,决定图片的转化;

合理的配置:

  • 小一点的图片:进行base64编码;

  • 大一点的图片:单独的图片打包,形成url地址,单独的请求这个url图片;

  • 配置webpack:官网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg|webp)$/, // 图片资源 (jpe?g表示jpg和jpeg格式)
// 打包图片
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 18 * 1024, // 18kb (小于18kb,转为base64;大于18kb,单独打包成文件)
},
},
},
],
},
};

Babel

  • webpack可以打包JS文件,但是ES6+的语法不会转化为ES5;
  • Babel是一个工具链,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;
  • 包括:ES6+,TypeScript、Vue、React等通过Babel转换为向后兼容版本的JavaScript;
  • 在线Babel转换:官网

插件

Babel的使用,需要安装相应的插件(这里不过多介绍,一般来说安装预设preset即可);

1
2
3
# 基本不安装此插件
npm install @babel/plugin-transform-arrow-functions -D #箭头函数 转成 普通函数 插件
npm install @babel/plugin-transform-block-scoping -D # const/let 转成 var 插件

Babel的预设preset

集成了Babel常用的插件;

  • 安装
1
2
npm install @babel/preset-env -D  #安装预设preset
npm install babel-loader -D #安装babel-loader
  • 使用
1
2
3
4
5
6
7
8
9
10
11
12
//webpack.config.js
module.exports = {
module: {
rules: [
{
test:/\.js$/,
use:["babel-loader"],
}
],
},
};

1
2
3
4
// 新建babel.config.js
module.exports={
presets:["@babel/preset-env"],
}
  • 入口文件
1
2
3
4
5
6
7
// // src/index.js
// import "./components/div_cpns.js"; // 模块引入;添加到依赖图中 (div_cpns.js模块没有进行导出)

// import "./components/img_cpns.js"; // 模块引入;添加到依赖图中 (img_cpns.js模块没有进行导出)

const message="hello babel";
message.split(" ").map((item)=>console.log(item))
  • 打包npm run build,查看打包之后的main.js;(箭头函数转化为了普通函数)
1
"hello babel".split(" ").map((function(l){return console.log(l)}));

Plugin

  • Loader是用于特定的模块类型进行转换;(文件解析、转换)
  • Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;(文件解析、转换之外的所有事情);
  • 官网

CleanWebpackPlugin

每次修改了一些配置,重新打包时,都需要手动删除dist文件夹,CleanWebpackPlugin可以自动化删除(需要配置输出目录)。

Tip:5.20.0+的版本,可以直接配置output==>官网

  • 安装
1
npm install clean-webpack-plugin -D
  • 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
// 必须要指定输出目录位置,否则不会自动清除;
output: {
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"], // postcss先对css进行处理,所以写在后面(从后往前解析)
},
],
},
// 使用插件(必须要指定输出目录位置,否则不会自动清除)
plugins: [new CleanWebpackPlugin()],
};
  • 测试

在之前打包的dist文件夹中,随便新建个文件,比如abc.js==>打包之后被清除;