一文读懂 webpack 配置选项 output.publicPath
引出问题
入口文件为:
// src/index.js
import path from './assets/webpack.png';
console.log(path);
const img = document.createElement('img');
img.src = path;
document.body.appendChild(img);
配置文件为:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
output: {
filename: 'scripts/[name].[chunkhash:3].js',
clean: true
},
devServer: {
open: 'html/index.html',
static: './dist'
},
plugins: [
new HtmlWebpackPlugin({
filename: 'html/index.html'
})
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:3].[ext]'
}
}
}
]
}
}
打包后 dist 目录的结构为:
dist
|—— img
|—— webpack.xxx.png
|—— scripts
|—— main.yyy.js
|—— html
|—— index.html
打包生成的 dist/html/index.html 文件:
该文件由 html-webpack-plugin 生成,作为一个插件,它知道最终输出的文件在 dist 目录中的位置,因此能够正确引入
<!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="../scripts/main.yyy.js"></script>
</head>
<body>
</body>
</html>
运行 dist/html/index.html 文件(请求 http://localhost:8080/html/index.html
页面),浏览器控制台输出:img/webpack.xxx.png,该相对路径最终转换成的绝对路径为 http://localhost:8080/html/img/webpack.xxx.png
,图片找不到
问题出现的原因
src/assets/webpack.png 图片会交给 file-loader 处理,作为一个 loader,它在运行时,webpack 还未输出文件到 dist 目录,因此,它只知道最终生成到 dist 目录中的图片路径为 img/webpack.xxx.png(配置该 loader 时指定的),并不知道该图片将来会被哪个文件使用
解决方法
// webpack.config.js
module.exports = {
// ...
output: {
// ...
publicPath: '/'
}
}
output.publicPath 本质就是一个字符串,并不会影响 webpack 的打包构建过程,只不过该字段的值会被某些 plugin 和 loader 读取使用,例如 html-webpack-plugin、file-loader 等等。
配置 output.publicPath 后:
- html-webpack-plugin 生成的 html 页面中,会将该值作为 script 标签和 link 标签引入的资源路径的前缀
- file-loader 在处理文件时,会将该值作为输出路径的前缀
- ...etc
所以配置了 output.publicPath="/"
后,file-loader 处理 webpack.png 图片时,输出的路径为:/img/webpack.xxx.png
,转换为绝对路径后为:http://localhost:8080/img/webpack.xxx.png
,路径正确,能够正常找到图片
扩展
若某些 plugin 和 loader 需要使用不同的资源路径前缀,那么可以为这些 plugin 和 loader 分别配置 publicPath,以 file-loader
和 html-webpack-plugin
为例:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
// ...
publicPath: "abc"
})
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
use: {
loader: 'file-loader',
options: {
// ...
publicPath: "bcd"
}
}
}
]
}
}
tip:并不是所有的 plugin 和 loader 都可以配置自己的 publicPath,具体可以参考其 npm 文档