这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
Webpack 知识体系
什么是 Webpack
前端项目由 PNG、JPG、GIF、WEBP、JS、TS、css、Less、Vue、JSX、Sass... 等资源构成。我们可以对这些资源进行手动管理,但会存在以下问题:
- 依赖手工,比如有 50 个 JS 文件... 操作,过程繁锁
- 当代码文件之间有依赖的时候,就得严格按依赖顺序书写
- 开发与生产环境一致,难以接入 TS 或 JS 新特性
- 比较难接入 Less、Sass 等工具
- JS、图片、CSS 资源管理模型不一致
这些都是旧时代非常突出的问题,对开发效率影响非常大,直到出现了很多工程化道具,例如 gulp、webpack、vite 等。
webpack 本质上是一种前端资源编译、打包工具,它拥有以下功能:
- 多份资源文件打包成一个 Bundle
- 支持 Babel、Eslint、TS、CoffeScript、 Less、 Sass
- 支持模块化处理 css、图片等资源文件
- 支持 HMR + 开发服务器支持持续监听、持续构建
- 支持代码分离
- 支持 Tree-shaking
- 支持 Sourcemap
- ...
使用 Webpack
1.安装
npm i -D webpack webpack-cli
2.编辑配置文件
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']
}]
}
}
3.执行编译命令
npx webpack
Webpack 打包核心流程
Webpack 打包核心简化版流程:
Entry => Dependencies Lookup => Transform => Bundle => Output
-
入口处理(
entry)- 从
entry文件开始,启动编译流程
- 从
-
依赖解析(
requireimport)- 从
entry文件开始,根据requireorimport等语句找到依赖资源
- 从
-
资源解析(
moudleoutput)- 根据
moudle配置,调用资源转移器,将 png、css 等非标准 JS 资源转译为 JS 内容
- 根据
-
资源合并打包(
output)- 将转译后的资源内容合并打包为可直接在浏览器运行的 JS 文件
其中 2、3 步递归调用,直到所有资源处理完毕
关于 Webpack 的使用方法,基本都围绕“配置”展开, 而这些配置大致可划分为两类:
- 流程类:作用于流程中某个 or 若干个环节,直接影响打包效果的配置项
- 工具类:主流程之外,提供更多工程化能力的配置项
示例:
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {
HotModuleReplacementPlugin
} = require('webpack' )
module.exports = {
entry: "./src/index",
mode: "development",
devtool: false,
output: {
filename: "[name].js",
path: path.join(__dirname, "./dist"),
clean: true,
},
devServer:{
hot: true,
open: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
title: 'Hot Module Replacement!',
}),
]
};
配置总览
按使用频率排序:
-
entry/ouputmodule.export = { entry: "./src/index", output: { filename: "[name].js", path: path.join(__dirname, "./dist"), } } -
moudle/plugins -
mode -
watch/devServer/devtool
处理 css
项目文件结构:
-
安装 Loader
npm add -D css-loader style-loader -
添加
module处理 css 文件module.export = { entry: './src/index', output: { filename: '[name].js', path: path.join(__dirname, './dist') }, module: { // css 处理器 rules: [ { test: /.css/i, use: ['style-loader', 'css-loader'] } ] } } -
引入 css 模块
const style = require('./index.css'); // or import style from './index.css'
接入 Babel
-
安装依赖
npm i -D @babel/core @babel/preset-env babel-loader -
声明产物出口
outputmodule.export = { 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']] } } ] } ] } } -
使用 ES6 语法
class Person { constructor () { this.name = 'Tecvan' } } console.log(new Person().name) const say = () => {} -
执行
npx webpack
生成 HTML
-
安装依赖
npm i -D html-webpack-plugin -
声明产物出口
entryconst 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
初始目录结构:
打包后的目录结构:
生成的 index.html
!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script defer src="main.js"></script>
</head>
<body>
</body>
</html>
HMR
Hot Module Replacement(HMR),模块热替换,用于提高开发效率。
使用方法:
-
开启 HMR
module.exports = { // ... devServer: { hot: true } } -
启动 Webpack
npx webpack serve
Tree-Shaking
Tree-Shaking,摇树,顾名思义,用于删除 Dead Code。
Dead Code 特征:
- 代码没有被用到,不可到达
- 代码的执行结果不会被用到
- 代码只读不写
- 模块导出了,但未被其他模块使用
- ...
开启 Tree-shaking:
module.exports = {
entry: './src/index',
mode: 'production',
devtool: false,
optimization: { usedExports: true }
}
进阶篇:理解 Loader
为了处理非标准 JS 资源,设计出资源翻译模块 —— Loader,用于将资源翻译为标准的 JS。
使用 Loader
-
安装 Loader
npm add -D css-loader style-loader less-loader -
添加
modoule处理 css 文件module.exports = { module: { rules: [ { test: /.less$/i, use: ['style-loader', 'css-loader', 'less-loader'] } ] } } -
使用 css / less / sass
import styles from './a.less';
Loader 的链式调用
- less-loader:实现 less => css 的转换
- css-loader:将 CSS 包装成类似 module.exports ="${css}"的内容,包装后的内容符合 JavaScript 语法
- style-loader:将 css 模块包进 require 语句,并在运行时调用 injectStyle 等函数将内容注入到页面的 style 标签
特点:
- 链式调用
- 支持异步执行
- 分 normal、pitch 两种模式
常见 Loader
进阶篇:理解插件
许多知名的工具,如:VS Code、Web Storm、Chrome、Firefox、Babel、Webpack、Rollup、Eslint、Vue、Redux、Quill、Axios 等等,都设计了”插件“架构。
Webpack 编译过程:
Webpack 编译是一个特别复杂的过程,具有上手成本、功能迭代成本高和功能僵化等缺陷,而插件架构的精髓是对外扩展开放,对修改封闭。Webpack 本身的很多功能都是基于插件实现的。
Webpack 插件是围绕着“钩子”展开的,钩子的核心信息如下:
- 时机:编译过程的特定节点,Webpack 会以钩子形式通知插件此刻正在发生什么事情;
- 上下文:通过 tapable 提供的回调机制,以参数方式传递上下文信息;
- 交互:在上下文参数对象中附带了很多存在 side effect 的交互接口,插件可以通过这些接 口改变。
学习路线
入门应用
- 理解打包流程
- 熟练掌握常用配置项、Loader、插件的使用方法,能够灵活搭建集成 Vue、React、 Babel、Eslint、Less、Sass、图片处理等工具的 Webpack 环境
- 掌握常见脚手架工具的用法,例如:Vue-cli、create-react-app、@angular/cli
进阶
- 理解 Loader、Plugin 机制,能够自行开发 Webpack 组件
- 理解常见性能优化手段,并能用于解决实际问题
- 理解前端工程化概念与生态现状
大师级
- 阅读源码,理解 Webpack 编译、打包原理,甚至能够参与共建