webpack 基本使用
- @Author: kevinlaizhiyu@foxmail.com
- @Date: 2025-02-08
- @Description: webpack 基本使用
为什么选择 webpack
浏览器中运行 JS 的方式:
-
- 引用脚本,难扩展,太多导致网络瓶颈;
-
- 包含所有项目代码的大型
js文件,作用域,文件大小,可读性,可维护性问题
- 包含所有项目代码的大型
依赖自动收集
webpack根据importrequire等关键字自动构建给予引用到处的依赖图谱。
主要功能
- 前端模块化。作用域污染,公共模块抽离;
- 代码压缩混淆。提升速度,防止代码盗用;
- 浏览器兼容性。babel-loader转es6为es5实现浏览器代码兼容;
- 性能优化。例如小图转
base64,css压缩;
基本配置
mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack webpack-cli
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
};
安装 webpack-cli 的情况下
{
"scripts": {
"build": "webpack --config webpack.config.js"
}
}
如果不想安装 webpack-cli 的情况下
npx webpack --config webpack.config.js
常用的 plugin 介绍
webpack 官网导航 Plugins 有简介
| plugin 名称 | 作用 | 来源 |
|---|---|---|
| html-webpack-plugin | 生成 html 文件 | npm |
| DefinePlugin | 定义全局常量 | webpack |
| HotModuleReplacementPlugin | 热更新 | webpack |
| clean-webpack-plugin | 清理 dist 目录 | npm |
| mini-css-extract-plugin | 分离 css | npm |
| uglifyjs-webpack-plugin | 压缩 js | npm |
| webpack-bundle-analyzer | 可视化 webpack 输出文件体积 | npm |
html-webpack-plugin
在 output 目录中生成 html 文件,在文件中使用 script 标签引入打包后的 js 文件
yarn add html-webpack-plugin -D // 安装
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
plugins: [new HtmlWebpackPlugin({})],
};
DefinePlugin
定义全局变量,应用:未开发环境和生产环境引用不同的配置
// ./webpack.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.DefinePlugin({
HOST: JSON.stringify("https://api.dev.com"),
}),
],
};
// ./webpack.prod.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.DefinePlugin({
HOST: JSON.stringify("https://api.prod.com"),
}),
],
};
// ./package.json 配置不同环境执行的命令
{
"scripts": {
"build:dev": "webpack --config webpack.config.js",
"build:prod": "webpack --config webpack.prod.config.js"
}
}
// ./index.js 此处 webpack 在编译时会替换掉对应的 HOST 值
function fetchList() {
fetch(HOST + "/users/octocat/repos");
}
常见的 loader
| loader | 作用 |
|---|---|
| css-loader | 加载 css 文件,能够在 JS 做文件中引入使用 |
| style-loader | 把 css 文件插入到 html 中,在 html 中通过 style 标签引入 |
| less-loader | less 文件转成 css 文件 |
| sass-loader | sass 文件转成 css 文件 |
| babel-loader | es6 转成 es5 |
| ts-loader | ts 转成 js |
| file-loader | 打包图片,打包字体图标 |
| url-loader | 和 file-loader 类似,当文件小于设定的 limit 的时候回,可以返回一个 DataUrl(提升网页性能 ) |
| html-withimg-loader | 打包 html 文件中的图片 |
| eslint-loader | eslint 检查 JS 代码错误,也可以用于代码规范 |
css-loader
# 两者必须同时使用
npm i css-loader style-loader -D
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
};
html {
background: pink;
}
// index.js
import "./style.css";
此时并没有生成 css 文件,而是在对应的 bundle.js 中插入了动态生成 style 标签的代码
打包优化- Code Splitting 代码分割
假设有两个入口文件 index.js another-module.js,同时在两个文件中引入一个第三方 npm 包 underscore , 结果是在两个文件中都对 underscore 进行了打包,应该提取公共部分,减少重复打包。
未优化的原始配置
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: {
index: "./src/index.js",
another: "./src/another-module.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
};
防止重复引入 (Prevent Duplication)
- 使用
shared(entry.shared) 属性
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: {
index: {
import: "./src/index.js",
dependOn: "shared",
},
another: {
import: "./src/another-module.js",
dependOn: "shared",
},
shared: "underscore",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
};
- SplitChunksPlugin
此项为一个 optimization 中的一个配置项 splitChunks, webpack 可以自己判断有哪些模块可以复用并抽离
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: {
index: "./src/index.js",
another: "./src/another-module.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
splitChunks: {
chunks: "all",
},
},
};
- 动态导入 Dynamic Import
import _ from "lodash";
const { default: _ } = await import("lodash");
- Caching 缓存
contenthas
output 的 filename 可以添加属性 [contenthash],当文件内容改变时会跟着一同改变,防止文件被浏览器缓存
// webpack.config.js
module.exports = {
entry: "./index.js",
plugins: [
new HtmlWebpackPlugin({
title: "Caching",
}),
],
outpout: {
filename: "[name].[contenthash].js", // 添加 [contenthash]
path: path.resolve(__dirname, "dist"),
clean: true, // 清除 dist 文件夹
},
};
- 提取文件范例
webpack提供了一个优化功能,可以使用optimization.runtimeChunk选项将runtime代码拆分为一个单独的chunk。将其设置为single来为所有的chunk穿件一个runtime buldle:
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: {
index: "./src/index.js",
another: "./src/another-module.js",
},
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
splitChunks: {
chunks: "all",
},
runtimeChunk: "single", // 提取 webpack runtime 代码
},
};
专业术语解释
runtimeChunk是 Webpack 中用于优化构建输出的一个配置项。它的主要作用是将 Webpack 的运行时代码(runtime)提取到一个单独的 chunk 文件中。运行时代码是 Webpack 用来管理和加载模块的引导代码,它负责解析模块依赖关系、执行懒加载等功能。通过将
runtimeChunk设置为"single",Webpack 会为所有生成的 chunks 创建一个单一的 runtime bundle。这有以下几个好处:
- 减少重复代码:多个入口文件或按需加载的 chunks 都会包含相同的运行时代码,将其提取到一个单独的文件可以避免重复。
- 缓存效率提升:由于 runtime 代码通常不会频繁变化,将其独立出来后,浏览器可以更高效地缓存这部分代码,而其他业务代码的变化不会影响到 runtime 文件的缓存。
- 简化依赖管理:当多个 chunks 共享同一个 runtime 时,它们之间的依赖关系更加清晰和一致。
生动形象的比喻
想象你正在组织一场大型音乐会,音乐会有很多不同的表演环节(类似于 Webpack 构建中的多个 chunks)。为了确保每个环节顺利进行,你需要有一个指挥来协调所有的演出安排(类似于 Webpack 的运行时代码)。
如果没有
runtimeChunk,每个表演环节都需要有自己的指挥,这样不仅浪费资源,而且容易出现混乱。但如果有一个统一的总指挥(相当于runtimeChunk: "single"),他可以统筹安排所有的表演环节,确保每个环节都能顺利进行,同时也不会因为某个环节的变化而影响到整个音乐会的协调工作。此外,这个总指挥的指令集(runtime 代码)相对固定,不需要经常更改,因此观众(浏览器)只需要记住一次总指挥的指令,下次再来观看时就不需要重新学习这些指令了,从而提高了整体效率。
希望这个解释能帮助你更好地理解
runtimeChunk的意义。