前言
很久之前就遇到过这个问题,一直在造轮子,难免会遇到一些库需要放图片作为背景图,上次是作为内联base64解决这个问题,但是也没有追寻为什么。这次又遇到了这个问题,应该仔仔细细弄个清楚,并花时间整理记录一下。
一、环境背景
- 打包工具: webpack5
- 系统: MacOS
- 发布到npm 公有镜像
这次是打包上篇文章的3d-earth 组件,threejs 需要一些纹理地图,为了简单引用就不让外部传入纹理图片,所以需要包内纹理打包图片。
webpack5 图片资源打包简介
在 webpack 5 之前,通常使用:
raw-loader 将文件导入为字符串 url-loader 将文件作为 data URI 内联到 bundle 中 file-loader 将文件发送到输出目录 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过
使用 url-loader,并且配置资源体积限制实现。 当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 ‘javascript/auto’ 来解决。
我使用的图片打包配置
代码语言:javascript复制{
test: /.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
type: "asset"
},
现在,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
打包完之后的组件包:
可以看出有图片已经打包成resource 处理了,如果将打包后的js直接放到Html 中引用也是没有问题的,路径也正确。
二、问题描述
问题步骤
- webpack5 打包dist
- 将dist发布到npm
- 使用umi 集成包或者其他经过webpack 打包工程集成
- 将会出现图片加载失败,已屏蔽图片,图片是一个
file://
本地连接
三、问题追踪、分析
问:为什么会是本地路径呢?
带着这个问题,一起看一个打包之后的代码
先看这一段:
代码语言:javascript复制/***/ "./src/img/earth_glow.png":
/*!********************************!*
!*** ./src/img/earth_glow.png ***!
********************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
eval("module.exports = __webpack_require__.p "9e4abf8518ca87b87326.png";nn//# sourceURL=webpack://3d-earth/./src/img/earth_glow.png?");
/***/ }),
不难看出最终图片输出的路径是:webpack_require.p 图片名 问:webpack_require.p 是什么路径呢?
__webpack_require是webpack 打包的核心函数,webpack_require.p一般是从 output.publicPath 读取的。
output.publicPath
可以参考官网了解详情 :传送门
重点看下常用几种类型:
代码语言:javascript复制 output: {
// One of the below
publicPath: 'auto', // It automatically determines the public path from either `import.meta.url`, `document.currentScript`, `<script />` or `self.location`.
publicPath: 'https://cdn.example.com/assets/', // CDN(总是 HTTPS 协议)
publicPath: '//cdn.example.com/assets/', // CDN(协议相同)
publicPath: '/assets/', // 相对于服务(server-relative)
publicPath: 'assets/', // 相对于 HTML 页面
publicPath: '../assets/', // 相对于 HTML 页面
publicPath: '', // 相对于 HTML 页面(目录相同)
},
如果什么不设置的时候也就auto
,这时候默认路径是 import.meta.url
import.meta
import.meta是一个给JavaScript模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的URL。
问题总述
不难看出,我没有设置output.publicPath,所以默认是auto
也就是import.meta.url 所以最后图片是一个本地file
路径
四、问题解决
问题分析出了原因,那么如果以后我们想在第三方包里加图片应该怎么办呢?
我最后归纳出了几种办法,如果有更好的欢迎留言:
- 将webapck 打包里的图片设置为
asset/inline
,这样打包内联成base64,就不会有路径问题了 - 设置图片为url连接动态加载,也就规避了这种问题
- 于2方案相同,更专业就是设置统一cdn连接。 publicPath: ‘//cdn.example.com/assets/’, // CDN(协议相同)
- 其实我觉得这个问题是不应该出现的,图片是否应该交给外部提供更好呢?所以留个属性接口由外部提供也能解决这个问题,这是属于设计层面的办法。
我一直觉得有更好的解决办法,欢迎留言区大佬赐教