Webpack[url/file-loader]无法解析URL的相对路径

12

我在Webpack中遇到了一个关于相对路径的问题。

让我试着解释一下这种情况:

我在工作目录中有两个独立的项目:

  1. Project-A [使用Gulp打包]:稳定并且可用
  2. Project-B [使用Webpack打包]:新项目

由于这两个项目都使用相同的样式,因此我想将Project A的SCSS文件[包含标准变量,预定义布局,模态框,类等]重用到Project B中。

enter image description here

现在,如果我尝试将Project-A index.scss作为另一个部分导入到Project-B index.scss中[注释掉背景图像URL依赖项],Webpack可以生成所需的CSS输出文件。

// Import Project A SCSS [Common Varibles, Classes, Styling etc] 
@import "../../../../Project_A/assets/stylesheets/index";

但由于Project-A的index.scss进一步引用了各自相对路径下的背景图片,webpack构建会报错:

'File / dir not found in XYZ/Project-B/Source/Stylesheets'。

精确的错误信息如下:

ERROR in ./src/assets/stylesheets/index.scss Module build failed: ModuleNotFoundError: 模块未找到:错误:无法解析 'file' 或 'diWorkSpace\Project_B\src\assets\stylesheets

屏幕截图: **enter image description here**

我无法理解,为什么Webpack无法解析Project-A内部资源的相对路径,而仍在Project B中查找。

这是模拟问题的代码库URL: https://github.com/raviroshan/webpack-build-issue/tree/master/WorkSpace

复现步骤:

  1. 下载代码库。
  2. 进入Project_B文件夹,并执行npm install。
  3. 运行“webpack”。它将正确构建,因为相对图像URL代码被注释掉了。
  4. 现在把代码的被注释掉的那行放回去:https://github.com/raviroshan/webpack-build-issue/blob/master/WorkSpace/Project_A/assets/stylesheets/index.scss#L27

我在我的 public/index.html 文件中添加了 <base href="/the/root/folder/" /> 来解决类似的问题(我喜欢这种解决方案,而不是再添加一个 npm 包)。 - Jacksonkr
3个回答

10

经过漫长的挣扎,终于得到了一个合适的解决方法

事实证明问题出在CSS加载器上,即它无法相对于当前文件解析URL。

使用resolve-url-loader解决了这个问题。 https://www.npmjs.com/package/resolve-url-loader

 // Old Loader Config in Webpack-entry
 loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!sass-loader?sourceMap')

 // New [Fixed] Loader Config in Webpack-entry
 loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!resolve-url-loader!sass-loader?sourceMap')

这是更新的代码存储库和解决方案:https://github.com/raviroshan/webpack-build-issue

注意:不要省略“-loader”。 您的 Webpack.config.js 应始终使用加载器名称的长格式(即“-loader”后缀)。

还有另一个名为 resolve-url 的包,Webpack 可能会将其与 resolve-url-loader 混淆。


我也在尝试让它工作。我正在将CSS导出到名为Content的文件夹中。然后,图像使用“Content / [hash] . [ext]”。这会将图像添加到〜/ Content中,但是当CSS查找图像时,它会在Content / Content / ... .png中查找。 - JonathanPeel

3
似乎是 css-loader 的问题,以及它在解析 @importurl() 路径的方式。它试图解析所有路径(即使是从导入样式表中的路径)相对于主 CSS 文件 —— 在您的情况下是 /Project_B/src/assets/stylesheets/index.scss

不要哭泣!有解决方案!

也许不是完美的,但这是我目前想到的最好的解决方案。

创建一个全局变量 $assetsPath,它保存与当前样式表相关的资源路径。然后将此变量添加到所有url()值之前。

在您的 /Project_A/assets/stylesheets/index.scss 中,您可以编写:

/*/ Using !default we can override this variable even before the following line: /*/
$assetsPath: '../' !default;

.container {
    /*/ ... /*/
    .content-wrapper {
        /*/ ... /*/
        background-image: url($assetsPath + "images/content-bg.jpg");
    }
}

在你的/Project_B/src/assets/stylesheets/index.scss文件中,你可以写入以下内容:
/*/ The following variable will override $assetsPath defined in the imported file: /*/
$assetsPath: '../../../../Project_A/assets/';

/*/ Import Project A SCSS [Common Varibles, Classes, Styling etc] /*/
@import "../../../../Project_A/assets/stylesheets/index";

后续影响

如果你使用Gulp来打包Project-A,那么它将会把Project-A的代码看作是:

        /*/ ... /*/
        background-image: url("../images/content-bg.jpg");

尽管如此,当您使用Webpack捆绑Project-B时,它会将Project-A的代码视为:
        /*/ ... /*/
        background-image: url("../../../../Project_A/assets/images/content-bg.jpg");

因此,你已经被拯救了。

我一定会更加仔细地研究这个问题。如果url-loader可以在@import语句中保留路径并相应地应用于引用的资产,则所有这些都可以避免。要么我漏掉了什么,要么应该将其视为错误。

祝你有美好的一天!
~Wiktor


你的方法可以行得通,但由于各种原因,我不想对Project_A代码库进行任何更改。我已经找到了一个更简单的解决方案,将其发布在这里... - Ravi Roshan

2

为了在file-loader中得到相对路径,您需要将publicPath设置为相对路径。


所以,我当前的公共路径代码是: publicPath: '/public/'即使我试图将其更改为任何相对路径,我仍然会得到相同的错误。publicPath: '... /../public/'请问您能否详细说明一下或者我是否遗漏了什么? - Ravi Roshan
我尝试参考https://github.com/webpack/file-loader/issues/46#issuecomment-258656602问题的核心是CSS相对于自身加载资产,而js相对于HTML加载资产。因此,如果CSS不在与HTML相同的位置,则无法使用相对路径。看起来似乎行不通。有任何替代方案吗? - Ravi Roshan
尝试将 "name=[path][name].[ext]" 添加到 file-loader 中。{ test: /.jpg$/, loader: "file-loader?name=[path][name].[ext]" } - Khalid Azam
这个不起作用。我猜你提供的解决方案是将URL链接到bundle.css。我的问题是 - 我无法构建Bundle本身。我将在我的问题中更新示例Codebase URL。 - Ravi Roshan
非常感谢!在我的情况下,我无法清除publicPath的config.publicPath部分。将其设置为“./”即可解决问题。 - Olav Kokovkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接