Webpack | 青训营

为什么要学习 Webpack

  • 理解前端"工程化"概念、工具、目标
  • 一个团队总要有那么几个熟悉 Webpack,某种程度上可以成为个人的核心竞争力
  • 高阶前端必经之路

什么是 Webpack

一个前端项目由很多的资源构成

旧时代的资源引入十分麻烦,对开发的效率影响很大

  • 依赖手动引入
  • 代码文件之间相互依赖时,需要严格按照依赖顺序书写
  • 比较难接入 Less、Sass 等工具
  • JS、图片、CSS 资源管理模型不一致

09年之后出现了很多的工具,"前端工程化"的概念逐渐出现

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

  • 多份资源文件打包成一个 Bundle
  • 支持 Babel、ESlint、TS、CoffeeScript、Less、Sass
  • 支持模块化处理 css、图片等资源文件
  • 支持 HMR + 开发服务器
  • 支持持续监听、持续构建
  • 支持 Tree-shaking
  • 支持 Sourcemap
  • ......

使用 Webpack

基本步骤

  1. 安装
bash 复制代码
npm install -D webpack webpack-cli
  1. 编辑配置文件

后续的配置可以做到非常非常复杂,实际上核心的地方就是这里的配置文件

js 复制代码
// webpack.config.js
const path = require('path')

module.exports = {
  entry: './src/index', // 入口
  mode: "development",
  devtool: false,
  output: {  // 产物出口
    filename: 'bundle.js',  // 产物名称
    path: path.join(__dirname, './dist')  // 产物路径
  } 
}

  1. 执行编译命令
bash 复制代码
npx webpack

Webpack 打包核心流程

  1. 入口处理

entry​ 文件开始,启动编译流程

  1. 依赖解析

entry​ 文件开始,根据 require​ 或 import​ 等语句找到依赖资源

  1. 资源解析

根据 module​ 配置,调用资源转移器,将 png、css 等非标准 JS 资源编译为 JS 内容

第二步和第三步实际上是递归循环执行的,也就是说解析资源的过程中可能遇到新的资源引入,从而触发新的依赖解析

  1. 资源合并打包

将转义后的资源内容合并打包为可以直接在浏览器运行的 JS 文件

效果:模块化和一致性

  • 多个文件资源合并成一个,减少 http 请求数
  • 支持模块化挨罚
  • 支持高级 JS 特性
  • 支持 Typescript、CoffeeScript 方言
  • 统一图片、CSS、字体 等其他资源的处理模型
  • ......

怎么使用 Webpack

基本上都是围绕 配置​ 展开的,这些配置大致可以分为两类:

  • 流程类:作用于流程中某个 or 若干个环节,直接影响打包效果的配置项
  • 工具类:主流程之外,提供更多工程化能力的配置项(比如 TreeShaking、devTool)

配置文件

webpack 可以设置配置文件 webpack.config.js​​

这个文件是在 node 中运行,遵循 node 的规范

mode

区分:src中需要在前端运行,因此使用前端规范,其他位置在 node 环境下运行,使用 node 规范

js 复制代码
// webpack.config.js
module.exports = {
    mode: "production"  // 设置打包模式为生产模式,另一个模式为开发模式 development
}

只打包有使用到的模块的部分代码,但是如果模块内有执行代码,则会整个模块都打包(webpack不知道有些属性/方法是否使用,因此整个模块都打包)

entry

配置打包时的主文件,默认为 "./src/index.js"​​

一般不改

可以传入数组,元素为多个打包的文件的路径字符串

可以传入对象,各属性名为打包后的文件名,各属性值为各文件的路径字符串

传入数组打包:将所有模块打包为一个文件,传入对象打包:多文件打包,分别打包各个模块

output

配置代码打包后的地址

传入一个对象

filename​​ 属性表示打包后的文件名,可以使用 "[name].js"​​ 在多文件打包时自动使用模块的名字,类似的还有 [id]​​ [hash]​​

clean: true​​ 打包前清空打包目录

path​​ 指定打包的目录(默认为 dist),绝对路径,最好在使用时引入 path 模块

处理 CSS

CSS文件 不在 JS 的模块化规范中,所以需要使用 Loader 进行处理(在 module 属性中配置)

安装处理 CSS 的 loader

bash 复制代码
npm add -D css-loader style-loader
js 复制代码
// in webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [{
      test: /\.css$/,  // 资源的正则匹配
      use: ['style-loader', 'css-loader']  // 使用的 Loader
    }]
  }
}

打包后,打包文件中会插入很多运行时的代码

接入 Babel

对高级的 JS 特性(ES6+)进行转义,让不支持高级特性的浏览器能供兼容这些 JS 代码

安装依赖

bash 复制代码
npm i -D @babel/core @babel/preset-env babel-loader

配置文件

js 复制代码
// in webpack.config.js
module.exports = {
  // ...
  module: {
    rules:[{
      test: /.\js$/,
      options: {
        presets: [
          {'@babel/preset-env'}  // 使用 preset-env
        ]
      }
    }]
  }
}

其余常用的 babel 依赖:

  • @babel/preset-react
  • @babel/preset-typescript

HTMLWebpack Plugin

用于直接生成一个 HTML 文件(index.html),并自动插入资源( JS 文件等)

js 复制代码
// in webpack.config.js
module.exports = {
  // ...
  plugin: [new HTMLWebpackPlugin()]
}

可以通过配置项配置更多功能

上面的三个配置可以归属为流程类配置,用于配置三板斧(HTML、CSS、JS)

下面开始介绍的是工具类的配置

HMR

Hot Module Replacement,模块热替换

更新的代码不需要刷新就能应用,主要在 devServer 这个配置项

js 复制代码
// in webpack.config.js
module.exports = {
  // ...
  devServer: {
    hot: true,  // 开启 HMR
    open: true,
  },
  watch: true, // webpack 持续监听代码修改
}

详细的原理可以看这篇文章:HMR 原理全解析

Tree-Shaking

Dead Code:

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

Tree-Shaking

  • 删除掉一些模块(模块导出了,但未被其他模块使用)
  • 删除 Dead Code

开启 tree-shaking 的一种方式

js 复制代码
// in webpack.config.js
module.exports = {
  // ...
  mode: "production", // 如果是"development"则不会 tree-shaking
  optimization: {
    useExports: true
  }
}

还有其他工具:

  • 缓存
  • Sourcemap
  • 性能监控
  • 日志
  • 代码压缩
  • 分包
  • ......

Loader

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

使用的步骤:

  • 安装需要的 loader
  • 在 webpack.config.js 中配置 module 中的 rules

loader 的链式调用

以 less-loader 举例

可以在 loader 的源码中插入 debugger;​,从而了解 loader 的执行过程

使用 ndb 工具,输入命令 ndb npm webpack​ 进行 debugger

通过 console​ 每一个 loader 的产出来观察转换的过程(一个 loader 的输出往往是另一个 loader 的输入)

loader 执行的阶段

loader 的标准模板

js 复制代码
// loader.js
module.exports = function(source, sourceMap?, data?) {
  // source 为 laoder 的输入
  // 可能是文件内容,也可能是上一个 loader 的处理结果
  return source;
};

常见的 loader

待后续学习补充

cache-loader

可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里。

file-loader

把文件输出到一个文件夹,在代码中通过相对 URL 去引用输出的文件(处理图片、字体、图标)

url-loader

和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去(大的文件由 file-loader 处理)

source-map-loader

加载额外的 Source Map 文件,方便断点调试

image-loader

加载并且压缩图片

babel-loader

把 ES6 转换成 ES5

css-loader

加载 CSS,支持模块化、压缩、文件导入等特性

style-loader

将 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS

sass-loader

将 scss/sass 代码转换成 css

less-loader

将 less 代码转换成 css

eslint-loader

通过 ESLint 检查 JavaScript 代码

Plugin

Webpack 的很多能力都是基于插件的

很多知名的工具都设计了"插件"架构

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

使用的过程:

  • 在 webpack.config.js 中引入插件
  • 在 webpack.config.js 的 pulgins 配置数组中新建插件对象
js 复制代码
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: "./src/index",
  output: {
    filename: "[name].js",
    path: path.join(__dirname, "./dist"),
  },
  plugins: [
    new HtmlWebpackPlugin(),
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
      VERSION: JSON.stringify('5fa3b9'),
    }),
  ]
}

Hook

插件围绕 钩子(Hook)​ 展开

可以打开 webpack 的源码来查看有哪些 Hook,以下是简化后常用的 Hook

钩子的核心信息:

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

编写 Plugin

Plugin 中的核心:

  • 时机:compiler.hooks.compilation
  • 参数:compilation
  • 交互:dependencyFactories.set

插件的编写是非常复杂的

可以参考以下文章:

常见的 Plugin

define-plugin

定义环境变量

ignor-plugin

忽略部分文件

clean-webpack-plugin

目录清理

html-webpack-plugin

简化 html 文件创建

web-webpack-plugin

方便地为单页面应用输出 html,比 html-webpack-plugin 好用

uglifyjs-webpack-plugin

通过 UglifyES 压缩 ES6 代码

optimize-css-assets-webpack-plugin

压缩 css 代码

webpack-parallel-uglify-plugin

多核压缩,提高压缩速度

webpack-bundle-analyzer

可视化 webpack 输出文件的体积

mini-css-extract-plugin

CSS 提取到单独的文件中,支持按需加载

HotModuleReplacementPlugin

模块热替换

如何学习 Webpack

知识体系图:

学习方法

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

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

  1. 大师级

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

入门应用的知识体系:

相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记