构建打包中静态资源URL处理
publicPath静态资源地址
建议配置
webpack
output: {
filename: 'js/[name].[contenthash].js',
chunkFilename: 'js/[name].[contenthash].js',
publicPath: 'auto',
}
入口文件, 必须在所有 import 之前
__webpack_public_path__ = process.env.CDN_BASE + '/';
CI/CD 注入、灰度发布
CDN_BASE=https://cdn.xxx.com/ npm run start
webpack 中配置 publicPath 后, 静态资源(含分包)会运行时拼接
__webpack_public_path__
哪些代码写法会
运行时拼接 __webpack_public_path__:
import / require / url() / HTML loader
+相对路径(./xxx / ../xxx)
运行时不拼接 __webpack_public_path__:
绝对路径(非相对路径的都是绝对路径,会由浏览器发起请求)
// 会拼: JS chunk 动态 import
const PageA = React.lazy(() => import('./PageA'));
// 会拼: 静态资源 import
import logo from './logo.png';
// 会拼: 会拼(因为可以静态解析)
import(`./assets/${value}.png`)
// 不会拼: 无法静态分析
import(path + ".png")
/* √: CSS url()中的 相对路径 经过 css-loader 处理 */
.logo { background-image: url('./logo.png'); }
.logo { background-image: url('../logo.png'); }
/* X: CSS url()中的 绝对路径 */
.logo { background-image: url('logo.png'); }
.logo { background-image: url('/logo.png'); }
.logo { background-image: url('https://xxx.com/logo.png'); }
另外提一句, qiankun框架
主应用的__webpack_public_path__大概率会和子应用不一致
所以需要 子应用 在自己入口文件配置 如下配置修改该值,
不然子应用运行时读到的是主应用的静态资源地址
// qiankun 将会在微应用 bootstrap 之前注入这个 publicPath
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// __INJECTED_PUBLIC_PATH_BY_QIANKUN__ 即对应 entry
registerMicroApps([
{
name: 'moduleName',
entry: 'https://cdn.example.com/moduleName/',
container: '#moduleName',
activeRule: '/moduleName',
},
])
URL处理配置
publicPath 对分包直接生效
img/font 需要 file-loader/url-loader(Webpack5 asset/resource)
CSS内url() 需要 css-loader
CSS 文件 需要 MiniCssExtractPlugin
Webpack4 需要如下配置
// module.exports={module:{rules:[]}};
{
test: /\.css$/i,
use: [
// 把解析后的 CSS 模块注入到 DOM
'style-loader', // 或 MiniCssExtractPlugin.loader 提取成单独文件
{
// 解析 @import 和 url() => require('./logo.png')
loader: 'css-loader',
options: { url: true },
},
],
}
{
test: /\.(png|jpg|gif|woff2?|eot|ttf|otf)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8 * 1024, // 小于8KB 转 base64
fallback: {
loader: 'file-loader',
options: {
name: '[name].[hash:8].[ext]',
// 效果:不要写死 publicPath,交给 __webpack_public_path__ 动态拼接
publicPath: 'auto',
},
},
},
},
],
}
Webpack5 资源模块 内置了 file-loader / url-loader 能力,不需要额外插件。
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: { maxSize: 8 * 1024 } // <8KB base64
},
generator: {
filename: 'img/[name].[hash:8][ext]',
publicPath: 'auto', // 支持 Runtime 动态 publicPath
},
}
rules匹配 从上到下 匹配到就结束, use loader顺序 从下到上
css-loader 会把 CSS 内的 url('./logo.png') 转换成 require('./logo.png')
转换后,Webpack 会把 ./logo.png 当作一个模块去解析, 再次遍历规则
注意:动态上下文打包 问题
import(`./assets/${value}.png`)
会触发 context module,也就是 动态上下文打包
assets 目录下所有 .png 都会被打包进 bundle
且会生成 JS wrapper 资源引用模块包
-- 打包后输出目录的资源文件夹
123.a.png
123.a.png.js <-- wrapper chunk
请改成下述写法以 避免生成 JS wrapper
const imgUrl = new URL(`./assets/${value}.png`, import.meta.url).href;
<img src={imgUrl} />
注意: publicPath 默认为 'auto'
❌ 1.不要将 publicPath 设置为 ''
✅ 2.配置CDN时,url末尾 必须有 '/'
publicPath 就是非常单纯的拼上其内容
output: {
filename: 'js/[name].[contenthash].js',
publicPath: '/',
}
// 产物 <script src="/js/main.1a2b3c.js">
output: {
filename: 'js/[name].[contenthash].js',
publicPath: '',
}
// 产物 <script src="js/main.1a2b3c.js">
output: {
filename: 'js/[name].[contenthash].js',
publicPath: 'https://cdn.xxx.com',
}
// 产物 <script src="https://cdn.xxx.comjs/main.1a2b3c.js">
打包后
✅ /img/main.1a2b3c.js 为 拼接根路径 的绝对路由
❌ img/main.1a2b3c.js 为 拼接当前路径 的相对路由
❌ cdn.xxx.comjs/main.1a2b3c.js 则直接格式出错
这时候会发现 页面空白