Skip to content

解决路径问题

问题复现

在使用 file-loader 或 url-loader 时,可能会遇到一个非常有趣的问题

比如,通过 webpack 打包的目录结构如下:

yaml
dist
    |—— img
        |—— a.png  #file-loader生成的文件
    |—— scripts
        |—— main.js  #export default "img/a.png"
    |—— html
        |—— index.html #<script src="../scripts/main.js" ></script>

我们看下 index.js 做了什么操作:

js
import imgPath from "./assets/img/webpack.png";
console.log(imgPath);

if (Math.random() < 0.8) {
  const img = document.createElement("img");
  img.src = imgPath;
  document.body.appendChild(img);
}

执行:

sh
pnpm run dev

我们发现,图片无法正确的显示,这是为什么呢?

问题分析

我们根据目录结构可知:

js
//js的运行路径是
localhost: 8000 / html / index.html;
//而我们看我们的图片的路径是:
imgs / webpack.png; //这是一个相对路径,会在当前文件夹下找imgs文件夹下的webpack.png,肯定是找不到的
//服务器找寻的真实路径:显然是多了一个/html,当然找不到了
localhost: 8000 / html / imgs / webpack.png;

//我们期待的图片地址是
localhost / imgs / webpack.png;

这种问题发生的根本原因:模块中的路径来自于某个 loader 或 plugin,当产生路径时,loader 或 plugin 只有相对于 dist 目录的路径,并不知道该路径将在哪个资源中使用,从而无法确定最终正确的路径

问题解决

面对这种情况,需要依靠 webpack 的配置 publicPath 解决

publicPath:配置公共资源路径。

该配置项本质上就是一个字符串,当配置了 publicPath 后,引用资源的路径就会在 webpack 打包的时候,将 publicPath 拼接到最开始的位置

webpack.config.js

js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  mode: "development",
  devtool: "source-map",
  devServer: {
    open: true,
    port: 8000,
    openPage: "html/index.html", //指定开发服务器打开的页面
  },
  entry: {
    index: "./src/index.js",
  },
  output: {
    filename: "scripts/[name]-[chunkhash:5].js", //使用相对路径,dist文件夹下生成scripts文件夹
    publicPath: "/", //+++配置静态资源引用路径,一般为'/',这样webpack打包出的js中,资源路径就会加上这个配置的字符串,然后服务器就会去找公共路径下的静态资源
  },
  //使用file-loader
  module: {
    rules: [
      {
        test: /\.(png)|(gif)|(jpg)$/,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "imgs/[name].[hash:5].[ext]",
            },
          },
        ],
      },
    ],
  },
  //
  plugins: [
    new CleanWebpackPlugin(),
    /*默认会在 dist 下生成 index.html,
    配置filename之后,会多出一个文件夹
    这个时候,就需要只当devServer中的openPage路径了。
     */
    new HtmlWebpackPlugin({
      template: "./public/index.html",
      filename: "html/index.html", //相对路径,dist下生成
    }),
    // new CopyPlugin([{ from: "./public", to: "./" }]),
  ],
};

打包出来的 dist/index.fs455.js

js
//片段,导入图片的打包后的结果

---
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/";


{
    /***/ "./src/assets/img/webpack.png": function (
      module,
      __webpack_exports__,
      __webpack_require__
    ) {
      "use strict";
      __webpack_require__.r(__webpack_exports__);
      //可以看到此处加上了__webpack_require__.p
      __webpack_exports__["default"] =
        __webpack_require__.p + "imgs/webpack.ba6ae.png";
}
---

扩展

我们有个疑问,为什么publikcPath一般为/

是因为我们在部署的时候,一般会将我们的前端资源部署在服务器的根目录下

js
//http://duyi.com/imgs/webpack.png
这就是会在服务器的根目录下找imgs文件夹下的webpack.png


//如果我们部署在了  http://duyi.com/website/imgs/webpack.png
那就代表,我们找的资源就部署在了服务器的website下,那么我们就需要将publicPath配置为"/website"

有些 loader 会提供 publicPath 配置项,loader 提供了的话,就与 webpack 的 publicPath 无关了,请注意这一点即可

MIT License