webpack 打包第三方库里有图片,集成包的时候图片变成本地路径加载不上,追寻了半天终于解决了困扰很久的问题。

2022-03-09 11:26:32 浏览数 (1)

前言

很久之前就遇到过这个问题,一直在造轮子,难免会遇到一些库需要放图片作为背景图,上次是作为内联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 中引用也是没有问题的,路径也正确。

二、问题描述

问题步骤

  1. webpack5 打包dist
  2. 将dist发布到npm
  3. 使用umi 集成包或者其他经过webpack 打包工程集成
  4. 将会出现图片加载失败,已屏蔽图片,图片是一个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路径

四、问题解决

问题分析出了原因,那么如果以后我们想在第三方包里加图片应该怎么办呢?

我最后归纳出了几种办法,如果有更好的欢迎留言:

  1. 将webapck 打包里的图片设置为asset/inline,这样打包内联成base64,就不会有路径问题了
  2. 设置图片为url连接动态加载,也就规避了这种问题
  3. 于2方案相同,更专业就是设置统一cdn连接。 publicPath: ‘//cdn.example.com/assets/’, // CDN(协议相同)
  4. 其实我觉得这个问题是不应该出现的,图片是否应该交给外部提供更好呢?所以留个属性接口由外部提供也能解决这个问题,这是属于设计层面的办法。

我一直觉得有更好的解决办法,欢迎留言区大佬赐教

0 人点赞