Webpack 知识体系 | 青训营笔记
这是我参与「第五届青训营」伴学笔记创作活动的第 13 天
0x1 Webpack 入门
-
什么是 Webpack
前端项目由资源构成,当资源较多且有一定的先后顺序时,引出了前端工程化工具,有了“前端工程”概念。
Webpack 是一种前端工程工具,本质上是一种前端资源编译、打包工具
-
Webpack 入门
-
实例
-
安装:
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
-
-
核心流程
-
入口处理
从 entry 文件开始,启动编译流程
-
依赖解析
从 entry 文件开始,根据 require 或 import 等语句找到依赖资源
-
资源解析
根据 module 配置,调用资源转移器,将 png、css 等非标准 JS 资源转译为 JS 内容
-
资源合并打包
将转译后的资源内容合并打包为可直接在浏览器运行的 JS 文件
-
-
模块化 + 一致性
- 多个文件资源合并成一个从而减少 HTTP 请求数
- 支持模块化开发
- 支持高级 JS 特性
- 支持 TypeScript、CoffeeScript 方言
- 统一 svg、css、font 等其他资源的处理模型
- ...
-
0x2 使用 Webpack
关于 Webpack 的使用方法,基本都围绕配置展开
- 流程类配置:作用于流程中某个或若干个环节,直接影响打包效果的配置项
- 输入:entry、context
- 模块解析:resolve、externals
- 模块转译:moudle
- 后处理:optimization、mode、target
- 工具类配置:主流程之外,提供更多工程化能力的配置项
-
一般文件结构
-
声明入口 entry
module.exports = { entry: "./src/index" };
-
声明产物出口 output
const path = require("path") module.exports = { entry: "./src/index", output: { filename: "[name].js", path: path.join(__dirname, "./dist") } }
-
运行
npx webpack
-
-
处理 CSS
-
在 src 目录下添加文件 index.css
-
在 index.js 文件中添加
const styles = require('./index.css')
-
安装 Loader:
npm add -D css-loader style-loader
-
添加 module 处理 CSS 文件
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" ] }] } }
-
-
接入 Babel
-
在 index.js 中添加
class Person { constructor() { this.name = 'Tecvan' } } console.log((new Person()).name) const say = () => {}
-
安装依赖:
npm i -D @babel/core @babel/preset-env babel-loader
-
声明产物出口 output
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 webpack
-
-
生成 HTML
-
安装依赖:
npm i -D html-webpack-plugin
-
声明产物出口 output
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 webpack
-
-
HMR
Hot Module Replacement(模块热替换)
-
开启 HMR
// filename: webpack.config.js moudle.exports = { // ... devServer: { hot: true } }
-
启动 Webpack:
npx webpack serve
-
-
Tree-Shaking
树摇,用于删除 Dead Code
- Dead Code
- 代码没有被用到,不可到达
- 代码的执行结果不会被用到
- 代码只读不写
- Tree-Shaking
- 模块导出了,但未被其他模块使用
- Dead Code
0x3 理解 Loader
-
Loader 的作用
Loader 是用于将资源翻译为标准 JS 的模块
-
使用 Loader
-
文件结构:
-
在 index.js 添加
import styles from './a.less'
-
安装 Loader:
npm add -D css-loader style-loader less-loader
-
添加 module 处理 CSS 文件
module.exports = { module: { rules: [{ test: /\.less$/i, use: [ "style-loader", "css-loader", "less-loader" ] }] } }
-
-
认识 Loader
-
链式调用
以上述 module 为例:
- less-loader:实现 less => css 的转换
- css-loader:将 CSS 包装成类似
module.exports = "${css}"
的内容,从而符合 JavaScript 语法 - style-loader:将 CSS 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签
-
其他特性
- 链式执行
- 支持异步执行
- 分 normal、pitch 两种模式
-
-
编写 Loader
-
eslint-loader/index.js
import getOptions from './getOptions' import Linter from './Linter' import cacheLoader from './cacheLoader' export default function loader(content, map) { const options = getOptions(this) const linter = new Linter(this, options) this.cacheable() if(options.cache) { cacheLoader(linter, content, map) return } linter.printOutput(linter.lint(content)) this.callback(null, contentm, map) }
-
loader.js
module.exports = function(source, sourceMap?, data?) { // source 为 Loader 的输入,可能是文件内容,或上个 Loader 的处理结果 return source }
-
-
常见 Loader
0x4 理解插件
插件架构精髓:对扩展开放,对修改封闭
-
插件围绕钩子展开
// filename: somePlugin.js class SomePlugin { apply(compiler) { compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {}) } }
钩子的核心信息:
- 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生声明事情(如:
compier.hooks.compilation
) - 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息(如:
compilation
等) - 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口插件可以提供这些接口改变(如:
dependencyFactories.set
)
- 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生声明事情(如:
0x5 学习路线
- 初级
- 理解打包流程
- 掌握常用配置项、Loader、插件的使用方法,能够灵活搭建集成工具的 Webpack 环境
- 掌握常见脚手架工具的用法
- 中级
- 理解 Loader、Plugin 机制,能够自行开发 Webpack 字间
- 理解常见性能优化手段并应用
- 理解前端工程化概念与生态环境
- 高级
- 理解 Webpack 编译、打包原理
- 参与共建