这是我参与【第四届青训营】笔记创作活动的第 12 天,今天学习了 Webpack 的一些知识点
1. 什么是 Webapck
前端项目由什么构成?
资源,包括常见的 PNG, JPG, GIF, WEBP, JS ,TS, CSS, LESS, Vue, Sass ...
可以手动管理这些资源:
但是
- 依赖手工,比如有50个JS文件要操作,流程复杂
- 当代码文件之间有依赖的时候,就得严格按依赖顺序书写
- 开发与生产环境一直,难以接入 TS 或 JS 新特性
- 比较难接入 Less, Sass 等工具
- JS、图片、CSS 资源管理模型不一致
这些都是旧时代非常突出的问题,对开发效率影响非常大,直到。。。
出现了很多 工程化 的工具
某种程度上正是这些工具的出现,才有了“前端工程” 这一个概念。
知道了Webpack诞生的目的,那我们再来看 webpack
webpack 本质上是一种前端资源编译、打包工具
他的很多功能
- 多份资源文件打包成一个
Bundle - 支持
- Babel、Eslint、TS、CoffeScript、Less、Sass
- 支持模块化处理 css、图片等资源文件
- 支持 HMR + 开发服务器
- 支持持续监听、持续构造
- 支持代码分离
- 支持 Tree-shaking
- 支持 SourceMap
- ...
2. Webpack 的打包核心流程
2.1 准备工作
- 安装
npm i -D webpack webpack-cli
- 编辑配置文件
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']
}]
}
}
- 执行编译命令
npx webpack
核心流程
模块化 + 一致性
多个文件资源合并成一个,减少 http 请求数
支持模块化开发
支持高级 JS 特性
支持 TypeScript、CoffeeScript 方言
支持图片、CSS、字体 等其他资源的处理模型
.....
2.2 使用 webpack
关于 webpack 的使用方法,基本都围绕“配置” 展开,而这些配置大致可划分为两类:
流程类:作用于流程中某个 or 若干个环节,直接影响打包效果的配置项
工具类:主流程之外,提供更多工程化能力的配置项
流程类配置
2.3 关键配置项介绍
2.3.1 配置总览
按使用频率
- entry / output
- module / plugins
- mode
- watch / devServer / devtool
2.3.1.1 文件结构
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
- 安装 Loader
npm add -D css-loader style-loader
- 添加配置
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"
]
}]
}
}
- 文件结构
index.js 中
const styles = require('./index.css')
// or
import styles from './index.css'
问题
- Loader 有什么作用?为什么这里需要用到 css-loader、style-loader
- 与旧时代——在 HTML 文件中维护 CSS 相比,这种方式会有什么优劣处
- 有没有接触过 Less、Sass、Stylues 这一类 CSS 预编译框架?如何在 webpack 接入这些工具?
参考资料
2.3.3 接入 Babel
- 安装
npm i -D @babel/core @babel/preset-env babel-loader
- 声明产物出口
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'
]
]
}
}]
}]
}
}
- 执行
npx webapck
2.3.4 生成 HTML
- 安装
npm i -D html-webpack-plugin
- 声明产物出口
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()]
}
- 执行
npx webapck
2.3.5 工具线
2.3.6 HMR
HMR( Hot Module ReplaceMent): 模块更替换,也是我们常说过的 “热更新” 的概念
- 开启 HMR
module.exports = {
devServer: {
hot: true
}
}
- 启动 webpack
npx webpack serve
2.3.7 Tree-Shaking
Tree-Shaking: 树摇,用于删除
Dead Code,即去掉没有作用的代码。之前我在看文章的时候看过这么一句话:TreeShaking就是在你秋天摇树的时候掉下来的枯叶就是
Dead Code,好的树叶还是会保留
Dead Code
- 代码没有被用到,不可到达
- 代码的执行结果不会被用到
- 代码只读不写
Tree-Shaking
- 模块导出了,但未被其他模块使用
开启 Tree-Shaking
mode: "production"optimization.usedExports: true
对 工具类库 如
Lodash有奇效
2.3.8 其他工具
- 缓存
- SourceMap
- 性能监控
- 日志
- 代码压缩
- 分包
- ...
3. Loader 组件
问题:Webpack 只认识 JS
const path = require('path')
module.exports = {
entry: "./src/index",
output: {
filename: "[name].js",
path: path.join(__dirname, "./dist")
}
}
为了处理非标准 JS 资源,设计成资源翻译模块 —— Loader 用于将资源翻译为 标准 JS
3.1 链式调用
less-loader: 实现less => css的转换css-loader: 将CSS包装成类似module.exports = "${css}"的内容,包装后的内容符合JavaScript语法style-loader: 将CSS模块包进require语句,并在运行时调用injectStyle等函数将内容注入到页面的style标签
3.2 其他特性
特点
- 链式执行
- 支持异步执行
- 分
normal,pitch两种模式
3.3 如何编写 Loader
3.4 常见 Loader
思考
- Loader 的输入是什么?要求的输出是什么?
- Loader 的链式调用是什么意思?如何串联多个 Loader?
- Loader 中如何处理异步场景?
4. Plugin 组件
4.1 什么是插件?为什么要这么设计?
这是 webpack 的编译流程
不难看出,这是一个特别复杂的过程,那么:
- 新人需要了解整个流程细节,上手成本高
- 功能迭代成本高,牵一发动全身
- 功能僵化,作为开源项目而言缺乏成长性
- ....
总结一下就是: 心智成本高 => 可维护性低 => 生命力弱
插件架构精髓:对扩展开放,对修改封闭
4.2 理解 插件
假设我们使用 html-webpack-plugin + DefinePlugin 插件
可以看出,用起来还是挺简单的,但是如果要自己开发一个插件,还是比较麻烦的
首先,插件围绕”钩子“展开
钩子的核心信息;
- 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生什么事情
- 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息
- 交互:在上下文参数对象中附带了很多存在
side effect的交互接口,插件可以通过这些接口改变
时机:
compier.hooks.compilation
参数:compilation 等
交互:dependencyFactores.set
5. 如何学习 Webpack
5.1 入门应用
- 理解打包流程
- 熟练掌握常用配置项、Loader、插件的使用方法,能够灵活插件集成 Vue、React、Babel、EsLint、Less、Sass、图片处理等工具的 Webpack 环境
- 掌握常见脚手架工具的用法:例如:
Vue-cli、create-react-app、@angular/cli
5.2 进阶
- 理解 Loader, Plugin 机制,能够自行开发 Webpack 组件
- 理解常见性能优化手段,并能用于解决实际问题
- 理解前端工程化概念与生态现状
5.3 大师级
- 阅读源码
- 理解 Webpack 编译、打包原理
- 甚至能够参与共建
总结
本文介绍了 webpack 的一些常用知识点,对于 Loader 和 Plugin 也做了一些介绍,希望能够帮助到正在学习 webpack 的同学~