Webpack基础(一)
什么是Webpack
模块化出现以后,代码被分离到各个模块中,由此一来,每个文件都需要请求服务器来获取,加载速度慢。Webpack就是为了解决这个问题。
从下面的图片可以看出,Webpack的主要功能就是把所有小文件打包成一个或多个大文件。将项目中的模块化代码(如 JavaScript、CSS、图片等资源)打包为可部署的静态文件。
除此之外,它还有代码分割、资源优化、提升开发体验等功能。
建议打开熟悉的项目的webpack配置文件,对照文件学习和理解。
安装依赖
安装webpack以及webpack-cli
webpack是核心包webpack-cli开发者与 Webpack 之间的桥梁,交互工具,提供命令行接口,让开发者可以调用 Webpack
设置运行模式
通过配置对象的 mode 字段设置运行模式,有三个可选项,默认为'none'
production:用于生产环境,优化输出代码的体积和性能development:用于开发环境,优化构建速度和调试体验none:不使用默认优化
具体的行为对比(扩展:帮助理解)
| 特性 | development | production | none |
|---|---|---|---|
| 代码压缩 | ❌ 不启用 | ✅ 启用 | ❌ 不启用 |
| SourceMap | ✅ 默认开启(eval 或 cheap-module-source-map) | ❌ 默认关闭 | ❌ 默认关闭 |
| 环境变量 | process.env.NODE_ENV = 'development' | process.env.NODE_ENV = 'production' | 不设置 |
| 优化 | 不进行性能优化 | ✅ 启用 Tree Shaking 和 Scope Hoisting | ❌ 不进行优化 |
| 打包速度 | 快 | 较慢(因为有优化) | 取决于具体配置 |
基本配置
- Webpack 使用
CommonJS模块语法导入导出 entry入口文件:表示从哪个文件开始解析依赖关系- output.path 必须是绝对路径,使用
path.join(__dirname, 'dist')可以动态生成绝对路径
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"), // 指定输出文件夹
},
};
Loader
Loader 就是将 Webpack 不认识的内容转换为认识的内容
Webpack 默认支持处理 JS 与 JSON 文件,其他类型的文件都处理不了,因此借助 Loader 进行各种其他类型文件的处理
引入样式文件 常用 loader:
css-loader: 将 CSS 转换为 JavaScript 模块,允许在 JavaScript 中导入 CSS
import "./styles.css";
style-loader: 将解析后的 CSS(由css-loader处理后)注入到页面的<style>标签里。(在开发环境中,通常用于在页面加载时将 CSS 直接注入,避免产生多余的文件请求)postcss-loader: 自动添加 CSS3 部分属性的浏览器前缀- 处理 less 或 sass 文件的 loader,比如 less 文件加载使用
less-loader
module.exports = {
// ...
module: {
rules: [
// 转换规则
{
test: /\.(le|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
},
],
},
};
一般在生产环境不使用 style-loader,而是使用 mini-css-extract-plugin,将 css 以文件的形式引入到页面上 use: [ // 'style-loader', MiniCssExtractPlugin.loader, // 添加 loader 可以在 plugin 中配置文件名称等 'css-loader', 'postcss-loader', 'sass-loader', ]
插件
插件可以贯穿 Webpack 打包的声明周期,执行不同的任务,常用的插件如下:
html-webpack-plugin:使打包后的 js 或 css 文件自动引入到 html 文件中
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: path.join(__dirname, "dist"), // 指定输出文件夹
},
module: {
rules: [
// 转换规则
{
test: /\.css$/, // 匹配所有的css文件
use: "css-loader", // use:对应的Loader名称
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
};
clean-webpack-plugin: 打包前将打包目录清空
区分环境
本地开发和部署线上,通常有不同的需求,需要在代码中判断出当前环境,可以通过cross-env设置环境变量
- 安装
npm install cross-env -D
- 设置环境变量,可以在
package.json脚本中设置环境变量
这里提到的环境变量是当前运行的 Node.js 应用的临时环境变量,程序关闭后失效,并不等同于电脑系统环境变量
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --mode development",
"test": "cross-env NODE_ENV=test webpack --mode production",
"build": "cross-env NODE_ENV=prod webpack --mode production"
}
- 在代码中使用环境变量
应用中设置的临时环境变量保存在
process.env中:
const curEnv = process.env.NODE_ENV; // 值为在脚本中设置的对应的值
cross-env的工作原理(扩展:帮助理解)cross-env本质上是一个命令行工具。它在后台会检查当前操作系统,并动态生成符合该系统规范的命令,然后运行你设置的脚本。它就是为了解决环境变量设置的跨平台兼容问题。
启动 devServer
直接打开 HTML 文件虽然可以展示内容,但无法支持模块化、跨域请求等现代开发需求。需要一个 HTTP 服务来托管代码。
devServer是 Webpack 的一个插件模块,由webpack-dev-server提供支持。它的核心功能是:
- 本地开发服务器:启动一个基于 Node.js 的 HTTP 服务器。
- 实时更新:结合 Webpack 的热更新(HMR, Hot Module Replacement)功能,可以在代码修改时自动刷新页面,或局部跟新代码,不需要手动刷新浏览器
- 代理服务:前端代码和后端接口通常运行在不同的域,跨域问题可能导致接口无法调用。通过 devServer 的代理配置,可以将 API 请求代理到后端服务器,解决跨域问题
- 优化开发体验:简化了手动编译和刷新流程,提升开发效率
通过devServer,可以在本地直接运行未打包的代码,同时看到实时效果
const devServer = "192.168.101.75";
module.exports = {
//...
devServer: {
// host: "localhost", // 只能在本机启动
host: "0.0.0.0", // 局域网内其他主机可以访问
port: 9999,
hot: true, // 开启HMR功能,用于开发环境
open: true, // 启动服务器时,浏览器打开默认访问地址
historyApiFallback: true, // 解决react-router刷新404问题,重定向到index.html,由客户端路由来处理这些请求
// 接口代理转发
proxy: [
{
context: ["/public", "/file", "/api"], // 当请求路径匹配到这些前缀时会被转发到target中指定的服务器地址
target: `http://${devServer}`,
changeOrigin: true, // 允许修改请求头中的 Origin,解决一些后端服务器要求的跨域验证问题
},
],
},
};
新特性:资源模块(Webpack 5)
webpack5 之前,需要file-loader、url-loader、img-loader等处理图片和字体文件,webpack5 新增了资源模块,允许使用资源文件(字体、图标等),而无需配置额外的 loader。
资源模块的type支持以下四种类型:
asset/resource
- 将资源打包为单独的文件,并导出文件的URL,类似于的
file-loader - 示例:
import flowerImg from './flower.png';
console.log(flowerImg); // 输出:生成后的路径,例如 /dist/image.png
asset/inline
- 将资源转为Data URL的形式直接内联到代码中,类似于
url-loader在limit小于某值时的功能 - 适合小文件,图标/SVG等,减少HTTP请求
- 示例:
import icon from './icon.svg';
console.log(icon);
// 输出:'data:image/svg+xml;base64,...'
asset/source- 将资源以源码字符串的形式导出,类似于
raw-loader - 适合需要直接使用文件内容的场景,例如读取Markdown文件、文本文件或SVG的原始XML数据
- 示例:
- 将资源以源码字符串的形式导出,类似于
import content from './example.txt';
console.log(content);
// 输出:'Hello, Webpack!'
asset- 自动在
asset/resource和asset/inline之间切换 - 可以自定义文件大小阈值阈值
- 示例:
- 自动在
import img from './image.png';
console.log(img);
// 如果小于 8 KB,输出:'data:image/png;base64,...'
// 如果大于 8 KB,输出:'http://example.com/image.png'
asset配置示例:
module.exports = {
// ...
module: {
{
test: /\.(jpe?g|png|gif)$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
// [ext] 自带 "." 这个与 url-loader 配置不同
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 50 * 1024 //超过50kb不转 base64
}
}
},
{
test: /\.(ttf|woff2?|map4|map3|avi|xlsx)$/,
type: "asset/resource", // 原封不动的输出
generator: {
filename: "static/media/[name]-[hash:6][ext]",
},
},
}
}
JS兼容性(Babel)
在开发中我们想使用最新的 Js 特性,但是有些新特性的浏览器支持并不是很好,所以 Js 也需要做兼容处理,常见的就是将 ES6 语法转化为 ES5。此时需要使用Babel。
- 安装依赖
babel-loader使用Babel加载ES2015+代码,并将其转换为ES5@babel/coreBabel编译的核心包@babel/preset-envBabel编译的预设
- 配置loader
const config = {
// ...
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
}
}
]
},
]
},
}
可以将Babel配置文件提取出来,在目录下新增.babelrc.js文件
SourceMap
SourceMap是一种映射关系,方便在代码出现问题时,定位到源码位置。
推荐配置:
开发环境:cheap-module-source-map调试体验好,性能较优
- cheap: 只定位到行,不定位列,生成速度快
- module: 适合模块化项目
生产环境 (none) 保护源码,放置泄露
hash
使用 Webpack 打包时,每个资源都可以生成一个带 hash 的路径。比如在基础配置中用到的filename: "[name][hash:8][ext]".。
为什么要使用 hash 呢?假设不使用 hash,并开启了缓存,则即使代码发生了修改,资源路径并没有改变,命中缓存,浏览器展示的始终是未改之前的内容。
除非关闭浏览器缓存,否则,每次文件更改,需要改变资源生成路径,来使浏览器资源更新。以便可以浏览到最新内容。
三种可选的 hash 类型
| 类型 | 定义 | 特性 | 适用场景 |
|---|---|---|---|
| hash | 基于整个项目的构建生成一个全局的哈希值 | - 每次构建时,所有文件的哈希值相同。 - 任意代码变动会导致整体哈希值变化 | - 输出非分块文件。如:index.html. - 不适合缓存优化 |
| chunkhash | 基于每个 chunk(代码块)的内容生成哈希值。 | - 每个 chunk 独立的哈希值,互不影响。 - 仅内容变更的 chunk 的哈希值会变化。 | - 分离的 JavaScript 文件,如vendor.js和main.js。- 用于优化缓存 |
| contenthash | 基于文件内容生成哈希值 | - 哈希值仅与文件自身内容相关。 - 文件内容不变时,哈希值保持不变。 | - 静态资源文件,如 CSS、图片等。 - 避免不必要的缓存更新。 |