这是我参与「第四届青训营」笔记创作活动的的第18天
什么是 Webpack
发展历程
前端项目由资源构成: PNG、JPG、GIF、WEBP、JS、TS、CSS、Less、Vue、JSX、Sass...
手动管理这些资源:
缺点:
- 依赖手工,比如有 50 个 JS 文件,操作,过程繁琐
- 当代码文件之间有依赖的时候,就得严格按依赖顺序书写
- 开发与生产环境一致。难以接入 TS 或 JS 新特性
- 比较难接入 Less、Sass 等工具
- JS、图片、CSS 资源管理模型不一致
这些都是旧时代非常突出的问题,对开发效率影响非常大,直到:
出现了很多工程化工具:
某种程度上正足这些工具的出现,才有“前端工程”这一概念
什么是 Webpack
Webpack 本质上是一种前端资源编译、打包工具
- 多份资源文件打包成一个 Bundle
- 支持 Babel、Eslint、TS、CoffeScript、 Less、Sass
- 支持模块化处理 CSS、图片等资源文件
- 支持 HMR + 开发服务器
- 支持持续监听、持续构建
- 支持代码分离
- 支持 Tree-shaking
- 支持 Sourcemap
使用 Webpack
安装配置
1. 安装
npm i -D webpack webpack-cli
2. 编辑配置文件
entry: 当前项目的入口
output: 当前项目打包完成后的文件名和位置
3. 执行编译命令
npx webpack
结果
核心流程
- 入口处理
从 "entry" 文件开始,启动编译流程
- 依赖解析
从 "entry" 文件开始,根据 "require" or "import" 等语句找到依赖资源
- 资源解析
根据 "module" 配置,调用资源转移器,将 png、css 等非标准 JS 资源转译为 JS 内容;
递归调用2、3,直到所有资源处理完毕
- 资源合并打包
将转译后的资源内容合并打包为可直接在浏览器运行的 JS 文件
模块化 + 一致性
- 多个文件资源合并成一个,减少 http 请求数
- 支持模块化开发
- 支持高级 JS 特性
- 支持 Typescript、CoffeeScript 方言
- 统一图片、CSS、字体等其它资源的处理模型
- ...
使用
关于 Webpack 的使用方法,基本都围绕 配置 展开,而这些配置大致可划分为两类:
- 流程类:作用于流程中某个 or 若干个环节,直接影响打包效果的配置项
- 工具类:主流程之外,提供更多工程化能力的配置项
流程类配置
配置总览
demo
开始
文件结构:
- 声明入口 'entry'
- 声明出口 'ouyput'
- 运行 'npx webpack'
处理 CSS
文件结构:
index.js:
- 安装 Loader
- 添加 'module' 处理 CSS 文件
结果:
接入 Babel 处理 JS
文件结构:
index.js:
- 安装依赖:
- 声明产物出口 'output':
- 执行 'npx webpack':
生成 HTML
文件结构:
- 安装依赖(插件):
- 声明产物出口 'output':
- 执行 'npx webpack':
工具线
HMR
Hot Module Replacement —— 模块热替换
- 开启 HMR:
- 启动 Webpack:
Tree-Shaking
Tree-Shaking:树摇,用于删除 Dead Code
Dead Code:
- 代码没有被用到,不可到达
- 代码的执行结果不会被用到
- 代码只读不写
- ...
开启 tree-shaking:
mode: "production"
optimization.usedExports: true
其他工具
- 缓存
- Sourcemap
- 性能监控
- 日志
- 代码压缩
- 分包
- ...
理解 Loader
问题:
Webpack 只认识 JS:
为了处理非标准 JS 资源,设计出资源翻译模块 —— Loador,用于将资源翻译为标准 JS
使用 Loader
文件结构:
index.js:
- 安装 Loader:
- 添加 'module' 处理 css 文件:
链式调用
- less-loader:实现less => css的转换
- css-loader:将 CSS 包装成类似 module.exports = "${css}" 的内容,包装后的内容符合 JavaScript 语法
- style-loader:将 css 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签
特性:
- 链式执行
- 支持异步执行
- 分 normal、pitch 两种模式
编写 Loader
标准 Loader 需要具备的内容:
常见 Loader
理解插件
为什么要设计插件
这是一个特别复杂的过程;
- 新人需要了解整个流程细节,上手成本高
- 功能迭代成本高,牵一发动全身
- 功能僵化,作为开源项目而言缺乏成长性
心智成本高 => 可维护性低 => 生命力弱
插件架构精髓:对扩展开放;对修改封闭
使用插件
安装 => 导入 => 创建实例:
demo
使用 html-webpack-plugin
使用 html-webpack-plugin + DefinePlugin
写插件
钩子
钩子:在编译的某个环节触发,某种程度上可以理解为 —— 事件
钩子的核心信息
- 时机:编译过程的特定节点。Webpack 会以钩子形式通知插件此刻正在发生什么事情;
- 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息;
- 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口,插件可以通过这些接口改变。
demo:
- 时机:
compier.hooks.compilation - 参数:
compilation等 - 交互:
dependencyFactories.set