构建 Webpack 知识体系 | 青训营笔记

120 阅读4分钟

这是我参与【第四届青训营】笔记创作活动的第 12 天,今天学习了 Webpack 的一些知识点

1. 什么是 Webapck

前端项目由什么构成?

资源,包括常见的 PNG, JPG, GIF, WEBP, JS ,TS, CSS, LESS, Vue, Sass ...

可以手动管理这些资源: image.png 但是

  • 依赖手工,比如有50个JS文件要操作,流程复杂
  • 当代码文件之间有依赖的时候,就得严格按依赖顺序书写
  • 开发与生产环境一直,难以接入 TS 或 JS 新特性
  • 比较难接入 Less, Sass 等工具
  • JS、图片、CSS 资源管理模型不一致

这些都是旧时代非常突出的问题,对开发效率影响非常大,直到。。。

出现了很多 工程化 的工具

某种程度上正是这些工具的出现,才有了“前端工程” 这一个概念。

image.png

image.png

知道了Webpack诞生的目的,那我们再来看 webpack

webpack 本质上是一种前端资源编译、打包工具

image.png

他的很多功能

  • 多份资源文件打包成一个 Bundle
  • 支持
    • Babel、Eslint、TS、CoffeScript、Less、Sass
  • 支持模块化处理 css、图片等资源文件
  • 支持 HMR + 开发服务器
  • 支持持续监听、持续构造
  • 支持代码分离
  • 支持 Tree-shaking
  • 支持 SourceMap
  • ...

2. Webpack 的打包核心流程

2.1 准备工作

  1. 安装
npm i -D webpack webpack-cli
  1. 编辑配置文件 webpack.config.js
module.exports = {
	entry: "main.js",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist"),
	},
	module: {
		rules: [{
			test: /^.less$/i,
			use: ['style-loader', 'css-loader', 'less-loader']
		}]
	}
}
  1. 执行编译命令
npx webpack

image.png

核心流程

image.png

模块化 + 一致性

  1. 多个文件资源合并成一个,减少 http 请求数

  2. 支持模块化开发

  3. 支持高级 JS 特性

  4. 支持 TypeScript、CoffeeScript 方言

  5. 支持图片、CSS、字体 等其他资源的处理模型

  6. .....

2.2 使用 webpack

关于 webpack 的使用方法,基本都围绕“配置” 展开,而这些配置大致可划分为两类:

流程类:作用于流程中某个 or 若干个环节,直接影响打包效果的配置项

工具类:主流程之外,提供更多工程化能力的配置项

image.png

流程类配置

image.png

2.3 关键配置项介绍

2.3.1 配置总览

image.png

按使用频率

  1. entry / output
  2. module / plugins
  3. mode
  4. watch / devServer / devtool
2.3.1.1 文件结构

image.png

2.3.1.2 声明入口 entry
module.exports = {
	entry: "./src/index"
}
2.3.1.3 声明产物出口 output
const path = require('path')
module.exports = {
	entry: "./src/index",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist")
	}
}
2.3.1.4 运行
npx webpack

2.3.2 处理 CSS

  1. 安装 Loader
npm add -D css-loader style-loader
  1. 添加配置
const path = require('path')
module.exports = {
	entry: "./src/index",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist")
	},
	module: {
		rules: [{
			test: /\.css/i,
			use: [
				"style-loader",
				"css-loader"
			]
		}]
	}
}
  1. 文件结构 image.png index.js 中
const styles = require('./index.css')
// or
import styles from './index.css'

问题

  1. Loader 有什么作用?为什么这里需要用到 css-loader、style-loader
  2. 与旧时代——在 HTML 文件中维护 CSS 相比,这种方式会有什么优劣处
  3. 有没有接触过 Less、Sass、Stylues 这一类 CSS 预编译框架?如何在 webpack 接入这些工具?

参考资料

2.3.3 接入 Babel

  1. 安装
npm i -D @babel/core @babel/preset-env babel-loader
  1. 声明产物出口
const path = require('path')
module.exports = {
	entry: "./src/index",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist")
	},
	module: {
		rules: [{
			test: /\.js?$/,
			use: [{
				loader: "babel-loader",
				options: {
					presets: [
						[
							'@babel/preset-env'
						]
					]
				}
			}]
		}]
	}
}
  1. 执行
npx webapck

2.3.4 生成 HTML

  1. 安装
npm i -D html-webpack-plugin
  1. 声明产物出口
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
	entry: "./src/index",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist")
	},
	plugins: [new HtmlWebpackPlugin()]
}

  1. 执行
npx webapck

2.3.5 工具线

image.png

2.3.6 HMR

HMR( Hot Module ReplaceMent): 模块更替换,也是我们常说过的 “热更新” 的概念

1.gif

  1. 开启 HMR
module.exports = {
	devServer: {
		hot: true
	}
}
  1. 启动 webpack
npx webpack serve

image.png

2.3.7 Tree-Shaking

Tree-Shaking: 树摇,用于删除 Dead Code,即去掉没有作用的代码。

之前我在看文章的时候看过这么一句话:TreeShaking就是在你秋天摇树的时候掉下来的枯叶就是 Dead Code,好的树叶还是会保留

image.png

Dead Code

  • 代码没有被用到,不可到达
  • 代码的执行结果不会被用到
  • 代码只读不写

Tree-Shaking

  • 模块导出了,但未被其他模块使用

image.png

开启 Tree-Shaking

  • mode: "production"
  • optimization.usedExports: true

对 工具类库 如 Lodash 有奇效

2.3.8 其他工具

  1. 缓存
  2. SourceMap
  3. 性能监控
  4. 日志
  5. 代码压缩
  6. 分包
  7. ...

image.png

3. Loader 组件

问题:Webpack 只认识 JS

const path = require('path')
module.exports = {
	entry: "./src/index",
	output: {
		filename: "[name].js",
		path: path.join(__dirname, "./dist")
	}
}

image.png

为了处理非标准 JS 资源,设计成资源翻译模块 —— Loader 用于将资源翻译为 标准 JS

3.1 链式调用

image.png

  • less-loader: 实现 less => css 的转换
  • css-loader: 将 CSS 包装成类似 module.exports = "${css}" 的内容,包装后的内容符合 JavaScript 语法
  • style-loader: 将 CSS 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签

3.2 其他特性

image.png

特点

  • 链式执行
  • 支持异步执行
  • normal, pitch 两种模式

3.3 如何编写 Loader

image.png

image.png

3.4 常见 Loader

image.png

思考

  • Loader 的输入是什么?要求的输出是什么?
  • Loader 的链式调用是什么意思?如何串联多个 Loader?
  • Loader 中如何处理异步场景?

4. Plugin 组件

4.1 什么是插件?为什么要这么设计?

这是 webpack 的编译流程

image.png

不难看出,这是一个特别复杂的过程,那么:

  • 新人需要了解整个流程细节,上手成本高
  • 功能迭代成本高,牵一发动全身
  • 功能僵化,作为开源项目而言缺乏成长性
  • ....

总结一下就是: 心智成本高 => 可维护性低 => 生命力弱

插件架构精髓:对扩展开放,对修改封闭

4.2 理解 插件

假设我们使用 html-webpack-plugin + DefinePlugin 插件 image.png 可以看出,用起来还是挺简单的,但是如果要自己开发一个插件,还是比较麻烦的

首先,插件围绕”钩子“展开 image.png

钩子的核心信息;

  1. 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生什么事情
  2. 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息
  3. 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口,插件可以通过这些接口改变

image.png

image.png 时机:compier.hooks.compilation 参数:compilation 等 交互:dependencyFactores.set

5. 如何学习 Webpack

5.1 入门应用

  • 理解打包流程
  • 熟练掌握常用配置项、Loader、插件的使用方法,能够灵活插件集成 Vue、React、Babel、EsLint、Less、Sass、图片处理等工具的 Webpack 环境
  • 掌握常见脚手架工具的用法:例如:Vue-clicreate-react-app@angular/cli

5.2 进阶

  • 理解 Loader, Plugin 机制,能够自行开发 Webpack 组件
  • 理解常见性能优化手段,并能用于解决实际问题
  • 理解前端工程化概念与生态现状

5.3 大师级

  • 阅读源码
  • 理解 Webpack 编译、打包原理
  • 甚至能够参与共建

总结

本文介绍了 webpack 的一些常用知识点,对于 Loader 和 Plugin 也做了一些介绍,希望能够帮助到正在学习 webpack 的同学~