Webpack从入门到放弃-----基础篇
hello!大家好我是小秃,从今天起我将会更新一个专栏 webpack那些事~ 来记录我个人系统学习Webpack时相关知识的总结,在这里我将会从基础到高级再到原理层面,尽可能的将我的所学所得总结出来,如果你也正在学习相关的知识,不妨看看哦,说不定对你有所帮助,加油!!! 温馨提示:篇幅过长,建议同学点击右侧目录按需阅读效率更高哦~~
什么是Webpack?
引用webpack官网的概念,本质上,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。
简单理解就是,当我们进行工程化开发时,往往在最后需要将开发环境下的代码进行整合挂入生产环境,这期间可能涉及代码优化分包、资源压缩等工作,或者为了使使用了某些技术书写的代码在实际载体中可以正常运行而需要进行编译(比如ES6语法代码需要编译为ES5才可以在浏览器中运行)Webpack就是帮助我们更好完成这些工作的。
其实除了Webpack,还有很多打包工具,比如Grunt、Gulp、Rollup、Parcel、Vite,这些都是非常流行的工程化打包工具,其中最为流行的就是Webpack,近些年随着Vue的崛起,Vite也开始备受关注
基本使用
正如官网定义中所说的,webpack 是一个静态模块打包工具,它是以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器段运行了,Webpack
输出的文件叫做 bundle
。
但是不可否认的是,单纯使用Webpack无论是在开发模式下还是生产模式下都是有局限性的
- 开发模式:仅能编译 JS 中的
ES Module
语法- 生产模式:能编译 JS 中的
ES Module
语法,还能压缩 JS 代码
所以为了增强Webpack的能力我们需要配合loader(加载器)和plugin(插件)使用,这也是我们使用Webpack的常规步骤之一,npm安装loader或plugin在配置文件中加载 其实如果同学们有了解或者使用过脚手架开发(React-cli/Vue-cli)的话其实你就已经使用过Webpack了,因为脚手架底层已经就是使用Webpack进行项目的搭建。 那么一个普通的Webpack项目中资源文件目录大概长这样:
webpack_code # 项目根目录(所有指令必须在这个目录运行)
└── src # 项目源码目录
├── js # js文件目录
│ ├── xxx.js
│ └── yyy.js
└── main.js # 项目主文件
是不是使用脚手架时你的代码和各种图片之类的资源也放在src中呢?在我们搭建webpack项目之前我们可以按照以上结构先创建文件夹及文件 简单的铺垫之后呢,我们正式开始使用
搭建Webpack项目
- 打开终端,来到项目根目录。运行以下指令:
npm init -y
这是为了初始化package.json
文件,这个文件将会保存你在之后通过npm引入的包映射,同时你也可以在其中进行一些诸如项目启动指令之类的配置,这里唯一要注意的是,需要注是 package.json
中 name
字段不能叫做 webpack
, 否则下一步会报错
- 下载依赖
npm i webpack webpack-cli -D
webpack不用再多说,webpack-cli就是搭建webpack项目的脚手架
- 启用 Webpack
npx webpack ./src/main.js --mode=development //启动开发模式
- 生产模式
npx webpack ./src/main.js --mode=production //启动生产模式
npx webpack
: 是用来运行本地安装 Webpack
包的。
./src/main.js
: 指定 Webpack
从 main.js
文件开始打包,不但会打包 main.js
,还会将其依赖也一起打包进来。
--mode=xxx
:指定模式(环境)。
默认Webpack会将文件打包输出到 dist
目录下,我们查看 dist
目录下文件情况就好了,观察我们可以发现所有文件被打包,并且都存在不同程度的处理,但其实,正如我们所说,webpack默认只能处理JS文件,而对于css、html等文件其实webpack本身无法处理,所以刚开始我们学习Webpack,我个人认为其实是在学习如何使用Webpack配合loader与plugin处理各种资源文件。
Webpack基本配置
来了来了他来了,终于废话(bushi)扯了一大堆,要讲到关键处了,要使用webpack,重中之重,也可以说是一切的前提,是要做好配置工作,webpack的所有配置都是写在一个名为webpack.config.js的配置文件中,这个文件就像是你项目的说明书,他会指明相应的资源在哪?,资源如何处理?,使用什么方式处理?。
配置文件的五大核心概念
1.entry(入口)
指示 Webpack 从哪个文件开始打包
2.output(输出)
指示 Webpack 打包完的文件输出到哪里去,如何命名等
3.loader(加载器)
webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析
4.plugins(插件)
扩展 Webpack 的功能
5.mode(模式)
主要有两种模式:
- 开发模式:development
- 生产模式:production
webpack.config.js就是由这五部分组成的,需要注意的是,Webpack 是基于 Node.js 运行的,所以采用 Common.js 模块化规范,同时不支持ES6,所以配置文件要使用ES5模块化语法编写
module.exports = {
// 入口
entry: "",
// 输出
output: {},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
mode: "",
};
在不加载任何loader与plugin的情况下,我们需要完成如下的配置
const path = require("path");//调用node.js中的path库获取文件的绝对路径
module.exports = {
entry: "./src/main.js",
output: {
//__dirname为path内置变量,他表示是当前文件夹,这里表示讲文件打包到当前文件夹下的“dist”文件夹
path: path.resolve(__dirname, "dist"),
filename: "main.js",//指明所有的js文件将被打包成一个main.js文件
},
module: {},
plugins: [],
mode: "development",//使用开发模式,也可以设置“production”生产模式
};
介绍完了基本的配置,我们将就webpack支持的开发模式与生产模式分开来讲,其实二者在配置上大同小异,只是因为就二者的使用场景,会有一些针对性的配置需要学习。
开发模式
开发模式顾名思义就是我们开发代码时使用的模式。 这个模式下我们主要做两件事:
-
编译代码,使浏览器能识别运行 开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源
-
代码质量检查,树立代码规范 提前检查代码的一些隐患,让代码运行时能更加健壮;提前检查代码规范和格式,统一团队编码风格,让代码更优美观。
处理样式资源
webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源 我们找 Loader 都应该去官方文档中找到对应的 Loader,然后使用 官方文档找不到的话,可以从社区 Github 中搜索查询。以打包CSS文件为例:
- 下载依赖
npm i css-loader style-loader -D
css-loader
:负责将 Css 文件编译成 Webpack 能识别的模块style-loader
:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容
此时样式就会以 Style 标签的形式在页面上生效
- 将如下字段加入到
module
配置项的rules
属性中,注意rules是一个数组
//……其他配置
module:{
rules:[
{
// 用来匹配 .css 结尾的文件
test: /.css$/,
// use 数组里面 Loader 执行顺序是从右到左
use: ["style-loader", "css-loader"],
},
]
}
以上是以处理CSS文件为例,很多时候我们还会使用less、sass、scss等预编译语言来处理样式,此时只需要下载相应的loader以及修改配置就好,如加载less文件资源
npm i less-loader -D //下载依赖
//修改配置
{
test: /.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
⚠注意,style-loader与css-loader是必须的,其次才是相应预编译语言的loader,实际上当执行打包命令npx webpack
之后,你会发现dist
文件夹下并没有css文件,则这是因为我们在配置时并没有做处理,所有引入的样式资源都被打包到了main.js
文件中
这就是我们打包到dist文件夹之后,main.js文件中的记录
处理图片资源
过去在Webpack4时,我们处理图片资源通过 file-loader
和 url-loader
进行处理
现在Webpack5已经将两个 Loader 功能内置到Webpack里了,我们只需要简单配置即可处理图片资源
依旧是在module
的rules
中添加
{
test: /.(png|jpe?g|gif|webp)$/,//使用正则表达式编写,表示针对以上格式的文件进行打包
type: "asset",//设置类型用于匹配模块。它防止了 `defaultRules` 和它们的默认导入行为发生
},
我们知道的是,在开发时,我们往往需要对图片等资源进行处理,宗旨就是保证一定质量的前提下,使用体积更小的图片,那么我们除了在使用的图片类型上做选择之外,我们其实针对某些小图片也可以使用base64转换的方式进行处理,在webpack中就可以进行简单配置来实现 图片资源的优化
{
test: /.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 小于10kb的图片会被base64处理
}
}
},
但是请注意,使用base64处理图片是有好有坏的,因为base64直接将图片编译为字符串嵌入代码中,这样其实是避免了发送请求从服务器资源中单独再请求图片,但是嵌入会导致html体积变大,拖慢整个页面的加载速度。
优化打包文件输出结构
以上我们讲解了图片与样式资源的打包与配置,已经发现了,代码相关的都被打包到了main.js
中,而图片也是直接被放置在了dist文件夹中,在实际开发中如果资源文件众多,这样是很不利于管理的,所以我们要将资源在dist
中的输出位置进行配置,将资源进行分类
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
//........其他配置
{
test: /.(png|jpe?g|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的图片会被base64处理
},
},
//使用generator字段配置图片的输出
generator: {
// 将图片文件输出到 static/imgs 目录中
// 将图片文件命名 [hash:8][ext][query]
// [hash:8]: hash值取8位
// [ext]: 使用之前的文件扩展名
// [query]: 添加之前的query参数
filename: "static/imgs/[hash:8][ext][query]",
},
},
这里Css
文件的处理我们要单独聊一下,Css 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式这样对于网站来说,会出现闪屏现象,用户体验不好我们应该是单独的 Css 文件,通过 link 标签加载性能才好
这需要我们下载相关的插件来处理
- 下载依赖
npm i mini-css-extract-plugin -S -D
这里我们使用Mini-css-extract-plugin
,这来自于webpack官网文档,更多配置可以查看文档内容,这里我们仅作简单演示
//因为是插件,所以我们同样需要先引入
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//......其他配置
{ //我们这里依旧以css文件为例
// 用来匹配 .css 结尾的文件
test: /.css$/,
// use 数组里面 Loader 执行顺序是从右到左
//可以发现我们去掉了style-loader,改用了插件自己的loader
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
//....其他配置
//在插件中引入,本质上webpack每个插件都是一个构造函数,使用new创建并配置
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
],
清理上次的打包资源
其实我们的打包每次都只需要留有最新一次的打包文件即可,这是我们可以配置在打包时自动清理上次的打包结果,使用clean
字段进行配置
output: {
path: path.resolve(__dirname, "dist"),
filename: "static/js/main.js",
clean: true, // 自动将上次打包目录资源清空
},
处理字体资源
HTML5的到来,使得html可以支持字体图标,字体图标顾名思义,是一种特殊的图标,它允许我们像操作文字一样,使用同样的css属性进行操作。webpack也可以对字体图标文件进行处理
{
test: /.(ttf|woff2?)$/,
type: "asset/resource",
generator: {
filename: "static/media/[hash:8][ext][query]",
},
},
字体文件的处理同样在rules
中进行配置。
但是要注意的是
type: "asset/resource"
和type: "asset"
的区别:
type: "asset/resource"
相当于file-loader
, 将文件转化成 Webpack 能识别的资源,其他不做处理type: "asset"
相当于url-loader
, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
处理js资源
虽然说webpack会直接处理js文件,但是只能编译 js 中 ES 模块化语法,不能编译其他语法,所以我们希望做一些兼容性处理。 其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。
-
针对 js 兼容性处理,我们使用 Babel 来完成
-
针对代码格式,我们使用 Eslint 来完成 我们先完成 Eslint,检测代码格式无误后,再由 Babel 做代码兼容性处理 关于ESlint与Babel因为不是我们的重点所以我们不多赘述,简单点说,ESlint会进行代码格式以及语法检查保证代码风格,Babel则是可以帮助我们将使用ES6等语法编写的代码转译为浏览器可以解读的代码,重点讲解相关的配置。 Eslint
-
.eslintrc.*
:新建文件,位于项目根目录.eslintrc
.eslintrc.js
.eslintrc.json
- 区别在于配置格式不一样
-
package.json
中eslintConfig
:不需要创建文件,在原有文件基础上写 ESLint 会查找和自动读取它们,所以以上配置文件只需要存在一个即可 我们以.eslintrc.js
配置文件为例:
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
- parserOptions 解析选项
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}
- rules 具体规则
"off"
或0
- 关闭规则"warn"
或1
- 开启规则,使用警告级别的错误:warn
(不会导致程序退出)"error"
或2
- 开启规则,使用错误级别的错误:error
(当被触发的时候,程序会退出)
rules: {
semi: "error", // 禁止使用分号
'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告
'default-case': [
'warn', // 要求 switch 语句中有 default 分支,否则警告
{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了
],
eqeqeq: [
'warn', // 强制使用 === 和 !==,否则警告
'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告
],
}
更多规则详见:规则文档 3. extends 继承 开发中一点点写 rules 规则太费劲了,所以有更好的办法,继承现有的规则。 现有以下较为有名的规则:
- Eslint 官方的规则open in new window:
eslint:recommended
- Vue Cli 官方的规则open in new window:
plugin:vue/essential
- React Cli 官方的规则open in new window:
react-app
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};
通过以上简单介绍,我们来看看具体在Webpack中如何使用
- 下载包
npm i eslint-webpack-plugin eslint -D
- 定义 Eslint 配置文件
module.exports = {
// 继承 Eslint 规则
extends: ["eslint:recommended"],
env: {
node: true, // 启用node中全局变量
browser: true, // 启用浏览器中全局变量
},
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
},
rules: {
"no-var": 2, // 不能使用 var 定义变量
},
};
- 修改webpack配置文件
//引入
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
//plugins中配置
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
}),
检验效果:
- 在
src
下新建js
文件夹,新建文件count.js
,并在其中暴露一个函数
// 简单做减运算
export default function(x,y){
return x-y
}
- 在main.js中引入
count.js
,并调用函数
import count from './js/count'
var result1 = count(2, 1);
console.log(result1);
- 执行打包操作
因为我们在配置文件中规定不可以使用
var
声明变量,并且我们规定他是一个错误级别为2,所以打包时报错终止了打包。
Babel 同Eslint一样配置文件有很多写法 配置文件有很多种写法:
-
babel.config.*
:新建文件,位于项目根目录babel.config.js
babel.config.json
-
.babelrc.*
:新建文件,位于项目根目录.babelrc
.babelrc.js
.babelrc.json
-
package.json
中babel
:不需要创建文件,在原有文件基础上写
我们以 babel.config.js
配置文件为例:
module.exports = {
// 预设
presets: [],
};
- presets预设 简单理解:就是一组 Babel 插件, 扩展 Babel 功能
@babel/preset-env
: 一个智能预设,允许您使用最新的 JavaScript。@babel/preset-react
:一个用来编译 React jsx 语法的预设@babel/preset-typescript
:一个用来编译 TypeScript 语法的预设
使用:
- 下载包
npm i babel-loader @babel/core @babel/preset-env -D
- 定义 Babel 配置文件
- babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
};
- 修改webpack配置
{
test: /.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
处理HTML资源
- 下载依赖
npm i html-webpack-plugin -D
2.配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
//.....其他配置
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "public/index.html"),
}),
- 修改 index.html 去掉引入的 js 文件,因为 HtmlWebpackPlugin 会自动引入
开发自动化配置---使用webpack-dev-server
我们可以发现的是,每次我们修改代码之后,需要手动执行命令才能重新解析文件,太麻烦了,我们希望一切自动化
- 下载包
npm i webpack-dev-server -D
- 配置
// 开发服务器
devServer: {
host: "localhost", // 启动服务器域名
port: "3000", // 启动服务器端口号
open: true, // 是否自动打开浏览器
},
注意以上配置是与之前提到的5大配置项同级的,不要写错位置哦,当你开启以上配置之后,我们执行的命令也有所变化:
npx webpack serve
当你执行该命令之后,你会发现,并没有出现平常看到的successfuly,终端也没有停止运行,这就说明了我们已经开启了一个服务器去监听我们的修改,有修改就会刷新页面,我们实时就可以看到修改结果了
生产模式
生产模式是开发完成代码后,我们需要得到代码将来部署上线。这个模式下我们主要对代码进行优化,让其运行性能更好。 优化主要从两个角度出发:
- 优化代码运行性能
- 优化代码打包速度
准备工作
我们分别准备两个配置文件来放不同的配置
- webpack.dev.js:开发模式配置文件
- webpack.prod.js:生产模式配置文件
- 文件目录
├── webpack-test (项目根目录)
├── config (Webpack配置文件目录)
│ ├── webpack.dev.js(开发模式配置文件)
│ └── webpack.prod.js(生产模式配置文件)
├── node_modules (下载包存放目录)
├── src (项目源码目录,除了html其他都在src里面)
│ └── 略
├── public (项目html文件)
│ └── index.html
├── .eslintrc.js(Eslint配置文件)
├── babel.config.js(Babel配置文件)
└── package.json (包的依赖管理配置文件)
- 修改 webpack.dev.js
因为文件目录变了,所以所有绝对路径需要回退一层目录才能找到对应的文件
// output部分
output: {
path: undefined, // 开发模式没有输出,不需要指定输出目录
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
// clean: true, // 开发模式没有输出,不需要清空输出结果
},
//plugins部分:
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
}),
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
template: path.resolve(__dirname, "../public/index.html"),
}),
],
运行开发模式的指令:
npx webpack serve --config ./config/webpack.dev.js
- 修改 webpack.prod.js
//output部分
output: {
path: path.resolve(__dirname, "../dist"), // 生产模式需要输出
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
clean: true,
},
//dev-server部分
//生产模式不需要dev-server
// devServer: {
// host: "localhost", // 启动服务器域名
// port: "3000", // 启动服务器端口号
// open: true, // 是否自动打开浏览器
// },
//使用生产模式
mode: "production",
运行生产模式的指令:
npx webpack --config ./config/webpack.prod.js
以上可以看出我们使用不同的配置,命令也不同,并且比较复杂,因此我们可以在package.json中进行命令的配置来简化命令,并且使命令更加语义化
- 配置运行指令
// package.json
{
// 其他省略
"scripts": {
"start": "npm run dev",
"dev": "npx webpack serve --config ./config/webpack.dev.js",
"build": "npx webpack --config ./config/webpack.prod.js"
}
}
以后启动指令:
- 开发模式:
npm start
或npm run dev
- 生产模式:
npm run build
Css 兼容性处理
当我们使用CSS样式是时,要考虑兼容性问题,那么我们就可以在打包时使用webpack提供的插件处理
- 下载依赖
npm i postcss-loader postcss postcss-preset-env -D
- 配置webpack.prod.js
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
注意,如果你使用的预编译语言写的样式,那么要将postcss-loader
放置在css-loader
下,xxx-loader
以上,这是因为webpack的loader解析顺序导致的
3. 控制兼容性
我们可以在 package.json
文件中添加 browserslist
来控制样式的兼容性做到什么程度。
{
// 其他省略
"browserslist": ["ie >= 8"]
}
想要知道更多的 browserslist
配置,查看browserslist 文档open in new window
以上为了测试兼容性所以设置兼容浏览器 ie8 以上,实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
{
// 其他省略
"browserslist": ["last 2 version", "> 1%", "not dead"]
}
以上配置的意思是,只兼容最新的两个版本,覆盖99%的浏览器,不兼容已经停止使用的浏览器版本
CSS压缩
- 下载包
npm i css-minimizer-webpack-plugin -D
- 配置
- webpack.prod.js
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
plugins: [
// css压缩
new CssMinimizerPlugin(),
],
注意,这里我们并没有对HTML或者JS代码进行压缩,那是因为在生产模式下,webpack会默认对以上两种文件进行压缩
总结
好啦,长篇大论的介绍了webpack对各种资源的压缩方式,使用的loader与plugin,如果你看过一遍,其实已经可以发现,从使用角度出发,webpack的工具属性体现的非常完美,他就是辅助我们进行各种资源的处理,提高代码的性能,其实就是从使用层面,webpack生态中的很多扩展我们都没有讲到,如果有兴趣的同学可以再去看下官网文档研究下哦。 今天我们只是简单的教给大家如何配置一个简单的webpack开发环境,如何针对不同资源做不同的处理以及如何优化处理,关于一些高级配置我们将在之后一一为大家讲解,感兴趣的同学请持续关注哦~,或者可以关注一下我的专栏Webpack那些事~ 我是小秃,如果喜欢我的内容的话,点个赞再走吧~
转载自:https://juejin.cn/post/7233004121682477114