Simonzhangs' blog Simonzhangs' blog
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • JS设计模式总结
  • 《Vue》
  • 《React》
  • 《TypeScript 从零实现 axios》
  • TypeScript
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • apple music
  • extension
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Simonzhangs

前端学习探索者
首页
  • 前端文章

    • HTML
    • CSS
    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • JS设计模式总结
  • 《Vue》
  • 《React》
  • 《TypeScript 从零实现 axios》
  • TypeScript
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • apple music
  • extension
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前端工程化

  • 性能优化

  • Webpack

    • Webpack基本使用
      • 什么是 Webpack
        • 追本溯源
        • Webpack 安装与使用
        • Webpack 基本参数配置与打包输出
      • webpack 打包静态资源
        • loader
        • loader 打包图片(file-loader 或 url-loader)
        • loader 打包 CSS
      • Webpack 核心配置(plugin 等)
        • html-webpack-plugin
        • clean-webpack-plugin
        • sourceMap 配置
        • WebpackDevServer + HMR(模块热更新)
        • 处理 ES6 语法
      • Webpack 进阶
        • tree shaking
        • 区分开发模式和生产模式
        • CodeSplitting
        • SplitChunksPlugin
        • Webpack 和浏览器缓存(hash 值)
      • 常见 webpack 面试题
        • 1.常见的 loader
        • 2.常见的 plugin
        • 3.loader 和 plugin 区别
        • 4.Webpack 构建流程
        • 5.sourceMap 是什么,怎么用
        • 6.HMR 热更新
      • ES6 模块与 CommonJS 模块差异
    • vue3+webpack从零开始配置
    • 我手写了一个loader
    • Tree shaking原理
  • 《综合性面试问题》
  • Webpack
simonzhangs
2022-05-11
目录

Webpack基本使用

# 什么是 Webpack

webpack 的核心概念是一个模块打包工具,它的主要目标是将 js 文件打包在一起,打包后的文件用于在浏览器使用,但它也能胜任转换(transform)、打包(bundle)、包裹(package)任何其他资源。

# 追本溯源

在面向过程阶段,会把 js 代码写在同一个 js 文件中,代码不容易理解和维护。

在面向对象开发阶段,把代码分模块书写再统一引入,这样解决了面向过程开发中的问题,但出现了新的问题:

  • 每个模块都需要引入一个 js 文件,随着模块增多,这会影响页面性能;
  • 在 index.js 文件,即引入的入口文件,并不能直接看出模块的逻辑关系,必须去页面才能找到;
  • 引入 js 的顺序必须严格按照顺序引入,因为 js 文件之间可能会进行 DOM 操作,顺序不能调换,否则会报错。

在现代开发模式,前端开发进一步工程化,采用模块化加载方案,包括:ES Module、AMD、CMD 和 CommonJS 等等。常用的前端模块化方案有 ES Module、Common JS。

但是浏览器并不能直接识别 ES Module 代码,需要借助其他工具来进行翻译,于是 Webpack 的出现就是为了解决这个问题。

# Webpack 安装与使用

包括全局安装和本地安装(推荐)。本地安装只在当前项目下有效,避免项目在不同电脑下因为版本不一样而不能正常打包。

// 本地安装
npm install webpack webpack-cli -D

npm install webpack webpack-cli --save-dev

// 查看webpack的历史版本记录
$ npm view webpack versions

// 按版本号安装
$ npm install webpack@4.25.0 -D
1
2
3
4
5
6
7
8
9
10

# Webpack 基本参数配置与打包输出

webpack.config.js是 Webpack 配置文件,基本配置参数如下:

  • entry:webpack 打包的入口
  • output:webpack 输出配置
    • filename 选项用来配置打包后的文件名;
    • path 选项配置打包后输出的目录文件夹。

改写 package.json 文件,添加执行脚本命令:

"scripts": {
    "bundle": "webpack"
},
1
2
3

打包输出项如下:

  • Hash:hash 代表本次打包的唯一 hash 值,每次打包此值都是不一样的;
  • Version:Webpack 版本号;
  • Time:本次打包耗时;
  • Asset:打包出的文件名称;
  • Size:打包出的文件大小;
  • Chunks:打包后的 js 文件对应的 id,从 0 开始,依次递增;
  • Chunks Names:打包后的 js 文件名字;
  • Entrypoint main = bundle.js:代表打包入口为 main
  • warning in configuration: 提示警告,需要配置 mode 属性
    • development 开发环境
    • production 生产环境
    • none 都不是
    • 默认为生产环境

# webpack 打包静态资源

# loader

对于静态资源,包括图片、css 等,Webpack 是怎么对它们进行打包的呢?是采用 loader,它是一种打包规则,告诉 Webpack 在遇到非 js 文件时候,应该如何处理这些文件。

loader 有如下固定运用规则:

  • 使用 test 正则来匹配相应的文件
  • 使用 use 来添加文件对应的 loader
  • 对于多个 loader 而言,从右到左依次调用
  • 使用 options 来增加额外的规则
  • 使用 exclude 来排除匹配的文件

# loader 打包图片(file-loader 或 url-loader)

安装:npm install file-loader -D 或者 npm install url-loader -D

webpack.config.js 配置:

const path = require("path");
module.exports = {
  // 其他配置
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: "file-loader",
          // 文件占位符 分别为原本文件名字、唯一编码、原本文件后缀
          options: {
            name: "[name]_[hash].[ext]",
          },
        },
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# loader 打包 CSS

CSS 样式文件分为多种情况,每种都需要不同的 loader 处理:

  • 普通 css 文件,使用 style-loader 和 css-loader
  • less 文件,使用 less-loader
  • sass 或 scss,使用 sass-loader
  • styl 文件,使用 stylus-loader

打包 CSS 文件的话,需要:

安装:$ npm install style-loader css-loader -D

webpack.config.js 配置:

// path为Node的核心模块
const path = require("path");
module.exports = {
  // ......
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"], // 从右到左的顺序调用,所以顺序不能错
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13

# Webpack 核心配置(plugin 等)

插件 plugin 是 webpack 的亮点,当 webpack 运行到某一个阶段,可以用 plugin 来帮我们做一些事情。

# html-webpack-plugin

可以让我们使用固定的模板,在每次打包的时候自动生成一个.html 文件,并且它会自动帮我们引入打包后的 js 文件。

安装:$ npm install html-webpack-plugin -D

配置:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  mode: "development",
  entry: {
    main: "./src/index.js",
  },
  plugins: [
    new htmlWebpackPlugin({
      template: "src/index.html",
    }),
  ],
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# clean-webpack-plugin

在打包之前,自动删除 dist 打包目录及目录下所有文件,不需要手动删除。

安装:$ npm install clean-webpack-plugin -D

配置:

const cleanWebpackPlugin = require("clean-webpack-plugin");
module.exports = {
  // ......
  plugins: [new cleanWebpackPlugin()],
};
1
2
3
4
5

# sourceMap 配置

一种映射关系,映射了打包后的代码和源代码之间的对应关系,通过 devtool 属性来配置。主要用来代码维护和调试找错方面。

  • 开发环境下(development):推荐将 devtool 设置成 cheap-module-eval-source-map
  • 生产环境下(production):推荐将 devtool 设置成 cheap-module-source-map

# WebpackDevServer + HMR(模块热更新)

webpack-dev-server 可以在源代码更改的情况下,自动打包代码并启动一个小型服务器,与热更新一块使用能够帮助我们高效开发。

  1. 安装:$ npm install webpack-dev-server -D

  2. package.json 配置:

// 其它配置
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",
    "dev": "webpack-dev-server'
  }
1
2
3
4
5
6
  1. webpack.config.js 配置
module.exports = {
  // 其它配置
  devServer: {
    // 以dist文件为基础启动一个服务器,服务器运行在4200端口上,每次启动时自动打开浏览器
    contentBase: "dist",
    open: true,
    port: 4200,
    hot: true, // 启用模块热更新
    hotOnly: true, // 模块热更新启动失败时,重新刷新浏览器
  },
};
1
2
3
4
5
6
7
8
9
10
11

HMR(模块热更新),能够在不刷新浏览器的前提下,运行时候能帮我们更新最新的代码,已内置在 Webpack 中了。

# 处理 ES6 语法

考虑低版本浏览器兼容性问题,需要把 ES6 代码转换成低版本浏览器能够识别的 ES5 代码。使用 babel-loader 和@babel/core 来进行 ES6 和 ES5 之间的链接,使用@babel/preset-env 来进行 ES6 转 ES5。

  1. 安装
// 安装 babel-loader @babel/core
$ npm install babel-loader @babel/core --save-dev

// 安装 @babel/preset-env
$ npm install @babel/preset-env --save-dev

// 安装 @babel/polyfill进行ES5代码补丁
$ npm install @babel/polyfill --save-dev
1
2
3
4
5
6
7
8
  1. webpack 配置
module.exports = {
  // 其它配置
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. .babelrc 文件

@babel/preset-env 需要在根目录下有一个.babelrc 文件,如下:

{
  "presets": ["@babel/preset-env"]
}
1
2
3

# Webpack 进阶

# tree shaking

树摇,用于移除项目中未使用的代码,只适用于 ES Module 语法(export、import),依赖于原发的静态结构特性。

启用:

const path = require("path");
module.exports = {
  mode: "production",
  optimization: {
    usedExports: true,
  },
};
1
2
3
4
5
6
7

# 区分开发模式和生产模式

分别书写公用配置文件,开发环境配置和生产环境配置文件,通过webpack-merge插件可以合并公共部分和私有部分。

# CodeSplitting

把较大文件,分离成更小的块,让浏览器进行并行加载,减轻大文件传输压力。配置如下:

module.exports = {
  // 其它配置
  optimization: {
    splitChunks: {
      chunks: "all",
    },
  },
};
1
2
3
4
5
6
7
8

chunks 属性:

  • async:此值为默认值,只有异步导入的代码才会进行代码分割。
  • initial:与 async 相对,只有同步引入的代码才会进行代码分割。
  • all:表示无论是同步代码还是异步代码都会进行代码分割。

# SplitChunksPlugin

可以用来做上面的代码分割,还有其他配置参数:

module.exports = {
  // 其它配置项
  optimization: {
    splitChunks: {
      chunks: "async",
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: "~",
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# Webpack 和浏览器缓存(hash 值)

为了防止浏览器端缓存了内容,而服务端对于文件内容进行修改后,浏览器端需要识别到,可以对文件进行 hash 标识,从而判断浏览器端缓存内容是否过期。

// 开发环境下的output配置还是原来的那样,也就是webpack.common.js中的output配置
// 因为开发环境下,我们不用考虑缓存问题
// webpack.prod.js中添加output配置
output: {
  filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].js'
}
1
2
3
4
5
6
7

# 常见 webpack 面试题

# 1.常见的 loader

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件(图片、字体)
  • url-loader:与 file-loader 相似,可以设置阈值
  • babel-loader:ES6 转换为 ES5
  • sass-loader:将 sass/scss 代码转换为 css
  • css-loader:加载 css,支持模块化、压缩、文件导入
  • style-loader:把 css 代码注入到 javascript 中,通过 DOM 操作去加载 css
  • postcss-loader:扩展 css 语法,配合 autoprefixer 插件自动补齐 css3 前缀

# 2.常见的 plugin

  • html-webpack-plugin:html 文件创建和引入 js
  • clean-webpack-plugin:目录清理
  • webpack-bundle-analyzer:可视化 webpack 输出文件的体积
  • webpack-merge:提取公共配置,减少重复配置代码

# 3.loader 和 plugin 区别

loader 本质上是一个函数,对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 javascript,所以 loader 成了翻译官,对其他类型的资源进行转译预处理。在 module.rules 中配置,类型为数组,每一项都是 Object,内部包含 test、loader、options 等属性。

Plugin 是插件,可以扩展 webpack 功能,利用 webpack 运行的生命周期中会广播出许多时间,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 api 改变输出结果。类型是数组,每一项都是一个 plugin 实例,参数都通过构造函数传入。

# 4.Webpack 构建流程

  • 初始化:启动构建,读取与合并配置参数,加载 plugin,实例化 compiler
  • 编译:从 entry 出发,针对每个 module 串行调用对应的 loader 去翻译文件的内容,再找到该 module 依赖的 module,递归地进行编译处理;
  • 输出:将编译后的 module 组合成 chuk,将 chunk 转换成文件,输出到文件系统中。

# 5.sourceMap 是什么,怎么用

source map 是将编译打包压缩后的代码映射回源代码的过程,方便调试源码。只要不打开开发者工具,浏览器是不会加载该文件。

# 6.HMR 热更新

HMR 特点是不用刷新浏览器便可以将新变更的模块替换掉旧的模块。

核心:客户端从服务端拉取更新后的文件,准确说是 chunk diff(chunk 需要更新的部分),实际上 WDS(webpack-dev-server)与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上次资源进行对比。

客户端对比出差异后会向 WDS 发起 ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 JSONP 请求获取该 chunk 的增量更新。

后续拉到增量更新内容后,由 HotModulePlugin 来完成,提供了相关 api 以供开发者针对自身场景进行处理。比如说 react-hot-loader 和 vueloader 都是借助这些 api 实现 HMR。

# ES6 模块与 CommonJS 模块差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用;
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口;
  • CommonJS 模块的 require()是同步加载模块,ES6 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段。

参考资料:

从今天开始,学习 Webpack,减少对脚手架的依赖(上) (opens new window)

「吐血整理」再来一打 Webpack 面试题 (opens new window)

Module 的加载实现 (opens new window)

编辑 (opens new window)
上次更新: 2022/06/16, 10:52:37
网站性能优化中,如何对小图片进行优化
vue3+webpack从零开始配置

← 网站性能优化中,如何对小图片进行优化 vue3+webpack从零开始配置→

最近更新
01
一些有意思的类比
06-16
02
the-super-tiny-compiler解析
06-06
03
计算机编译原理总概
06-06
更多文章>
Theme by Vdoing | Copyright © 2021-2022
蜀ICP备2021023197号-2
Simonzhans | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
  • 飙升榜
  • 新歌榜
  • 云音乐民谣榜
  • 美国Billboard榜
  • UK排行榜周榜