Webpack“OTS解析错误”加载字体

61

我的webpack配置指定使用url-loader加载字体,但当我在Chrome中尝试查看页面时,出现以下错误:

OTS parsing error: invalid version tag
Failed to decode downloaded font: [My local URL]

我的配置文件中的相关部分如下:

{
  module: {
    loaders: [
      // ...
      {
        test: /\.scss$/,
        loaders: ['style', 'css?sourceMap', 'autoprefixer', 'sass?sourceMap'],
      },
      {
        test: /images\/.*\.(png|jpg|svg|gif)$/,
        loader: 'url-loader?limit=10000&name="[name]-[hash].[ext]"',
      },
      {
        test: /fonts\/.*\.(woff|woff2|eot|ttf|svg)$/,
        loader: 'file-loader?name="[name]-[hash].[ext]"',
      }
    ],
  },
}

在Safari中不会出现这种情况,我还没有尝试过Firefox。

开发中我通过webpack-dev-server提供文件,在生产中它们被写入磁盘并复制到S3;在这两种情况下,我在Chrome中得到了相同的行为。

这也会发生在较大的图像上(大于图像加载器配置中的10kB限制)。


如果您的字体文件为空或损坏,您可能会遇到此问题。因此,请检查您的字体文件大小。 - Anil Upadhyay
14个回答

142

TL;DR 将你的资源(包括完整主机名)使用绝对路径,并将你的output.publicPath设置为例如 "http://example.com/assets/"。

问题

问题是 Chrome 解析动态加载 CSS blob 中的 URL 的方式。

在页面加载时,浏览器会加载你的 Webpack 捆绑入口 JavaScript 文件,该文件(当你使用 style-loader 时)还包含一个 Base64 编码的 CSS 副本,它被加载到页面中。

Chrome DevTools 中嵌入式 CSS 的截图 这是在 Chrome DevTools 中的样子

这对于所有以数据 URI 的形式编码到 CSS 中的图片或字体来说都没问题(即文件内容嵌入在 CSS 中),但对于通过 URL 引用的资源来说,浏览器必须查找和获取文件。

现在,默认情况下,file-loaderurl-loader 用于大型文件的代理)将使用相对 URL 引用资源 - 而这就是问题所在!

Webpack 生成的相对路径 URL 这些是默认情况下 file-loader 生成的 URL - 相对 URL

当你使用相对 URL 时,Chrome 将相对于包含 CSS 文件进行解析。一般来说,这没问题,但在这种情况下,包含文件位于 blob://...,任何相对 URL 都以同样的方式引用。结果是 Chrome 尝试从父 HTML 文件加载它们,并最终尝试将 HTML 文件解析为字体的内容,这显然行不通。

解决方案

强制 file-loader 使用包括协议("http" 或 "https")在内的绝对路径。

更改 webpack 配置以包含类似下面的东西:

{
  output: {
    publicPath: "http://localhost:8080/", // Development Server
    // publicPath: "http://example.com/", // Production Server
  }
}

现在生成的URL将会是这个样子:

enter image description here 绝对URL!

这些URL将被Chrome和其他浏览器正确解析。

使用extract-text-webpack-plugin

值得注意的是,如果你将CSS提取到一个单独的文件中,你不会遇到这个问题,因为你的CSS将会在一个正式的文件中,并且URL将会被正确解析。


1
哇,上周刚遇到了这个问题。当你有很多部署或开发环境时,手动更改IP地址是不是很繁琐呢? - Ben Orozco
2
@benoror,如果部署不使用style-loader,那么CSS将从单独的CSS文件中提供服务,并且Chrome将能够正确解析资源URL,因此您在例如部署应用程序并提取CSS的staging、test、QA环境中应该没问题。但是,如果您有许多开发环境,那可能会成为一个问题。至少在我们团队中,我们只在本地运行dev server一次,并从那里进行工作。这并不理想,我不得不通知我的队友更改配置,但这并不会真正影响我们。 - Will Madden
1
@WillMadden,我们遇到了一个相关的问题:https://github.com/shakacode/bootstrap-loader/pull/56/files#diff-d9789595ca7b71d22ec4bd1531c19267R43 如果您能查看一下重现案例,我们将不胜感激!我们遇到了完全相同的错误,并且正在使用extract-text-webpack-plugin。因此,“值得注意的是,如果您将CSS提取到单独的文件中,您将不会遇到这个问题,因为您的CSS将在正确的文件中,并且URL将被正确解析。”对我们不适用。 - justingordon
3
谢谢,我从你的回答中找到了导致我的SPA页面重新加载问题的原因。只是需要注意publicPath:'/'也可以起作用。 - Michael Buen
2
实际上,这个解决方案并没有解决我的环境问题。有没有办法将整个webpack配置和scss(相关块)上传到CodePen?谢谢! - Idan Gozlan
显示剩余9条评论

16

对我来说问题出在正则表达式上。以下代码使Bootstrap工作:

{
    test: /\.(woff|ttf|eot|svg)(\?v=[a-z0-9]\.[a-z0-9]\.[a-z0-9])?$/,
    loader: 'url-loader?limit=100000'
},

1
从测试中移除woff2,然后它就可以工作了... { test: /.(png|jpe?g|gif|svg|woff|ttf|eot|ico)$/, loader: 'file?name=assets/[name].[hash].[ext]' }, - Arun Kumar

13

如@mcortesi在这里所解释的,如果你从css加载器查询中删除sourceMaps,那么css将不会使用blob进行构建,数据url将被正确解析。


1
这个答案对我来说同样适用,就像上面更受欢迎的答案一样。 - Jeremy S.
谢谢您的回答。我在使用Storybook时遇到了Semantic UI字体无法加载的问题,花了很长时间寻找解决方案。这个方法解决了我的问题,只需要从CSS加载器中删除一个?sourceMap即可。 - greduan
完美!我不介意放弃源映射,我的构建需要在不事先指定域的情况下正常工作。 - Peter Mellett

5
与上面的@user3006381一样,我的问题不仅仅是相对URL,而且webpack将这些文件放置在它们像JavaScript文件一样的位置。它们的内容基本上都是:
module.exports = __webpack_public_path__ + "7410dd7fd1616d9a61625679285ff5d4.eot";

在字体目录中,实际上没有真正的字体文件,而是在输出文件夹下以哈希码形式存储的字体文件。为了解决这个问题,我需要更改我的url-loader测试(在我的情况下是我的图像处理器),以便不加载字体文件夹。但我仍然需要在webpack.config.js中设置output.publicPath,就像@will-madden在他的出色答案中所指出的那样。


这是我遇到的问题,但由于我正在使用 easy-webpack(可能命名错误),我不确定如何从 url loader 中排除字体文件夹。 - Darren Oster
@DarrenOster:我认为你必须使用它们的对象生成器将其指定为覆盖,以便您可以修改生成的配置。 - Adam McCormick

2
我遇到了同样的问题,但是原因不同。
在尝试了Will Madden提供的解决方案无效后,我通过互联网尝试了每一种备选方法。然而,这些方法都没有起作用。进一步探索后,我偶然打开了其中一个出现问题的字体文件。原始内容被Webpack覆盖,包含某种配置信息,可能是之前使用file-loader时的设置。我用原始文件替换了损坏的文件,问题就解决了(Chrome和Firefox都可以)。

1
最好且最简单的方法是将字体文件进行base64编码,然后在font-face中使用它。 要进行编码,请进入包含字体文件的文件夹并在终端中使用以下命令:
base64 Roboto.ttf > basecodedtext.txt

你将得到一个名为basecodedtext.txt的输出文件。打开该文件。删除其中的任何空格。
复制该代码并将以下行添加到CSS文件中:
@font-face {
  font-family: "font-name";
  src: url(data:application/x-font-woff;charset=utf-8;base64,<<paste your code here>>) format('woff');
}  

然后您可以在CSS中使用font-family: "字体名称"

比其他发布的解决方案更混乱,但它可以工作,并且在您没有直接访问webpack配置(例如,如果使用CRA)时非常有用。 - user2521119

1
我知道这并不能回答OP的确切问题,但我也遇到了相同的症状,不过原因不同:
我是这样引入Slick Slider的.scss文件的:
@import "../../../node_modules/slick-carousel/slick/slick.scss";

在仔细检查后,发现它试图从一个无效的位置 (<host>/assets/css/fonts/slick.woff) 加载字体,这是从样式表中引用的方式。
最终我只需将 /font/ 复制到我的 assets/css/ 中,问题就解决了。

1

1
自从你使用 url-loader 以来:

url-loader 的工作方式类似于 file-loader,但如果文件小于某个字节限制,则可以返回 DataURL。

因此,解决这个问题的另一个方法是将限制设置得足够高,使字体文件包含为 DataURL,例如设置为 100000,大约是 100Kb
{
  module: {
    loaders: [
      // ...
      {
        test: /\.scss$/,
        loaders: ['style', 'css?sourceMap', 'autoprefixer', 'sass?sourceMap'],
      },
      {
        test: /images\/.*\.(png|jpg|svg|gif)$/,
        loader: 'url-loader?limit=10000&name="[name]-[hash].[ext]"',
      },
      {
        test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
        use: 'url-loader?limit=100000&mimetype=application/font-woff',
      },
      {
        test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
        use: 'url-loader?limit=100000&mimetype=application/font-woff',
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: 'url-loader?limit=100000&mimetype=application/octet-stream',
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: 'file-loader',
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: 'url-loader?limit=100000&mimetype=image/svg+xml',
      },
    ],
  },
}

始终考虑限制数字的含义:

以Data URL形式内联文件的字节限制

这样,您无需指定资产的完整URL。当您希望Webpack不仅从本地主机响应时,这可能会很困难。

最后一点需要注意的是,此配置不建议用于生产环境。这只是为了方便开发而设置的。


0

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