使用Webpack和font-face加载字体

63

我正在尝试使用@font-face在CSS文件中加载字体,但字体从未加载。这是我的目录结构。

进入图像描述

然后在webpack.config.js中,我有一个加载字体的加载程序。

var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    "./index.js"
  ],
  output: {
    path: __dirname+"/build",
    filename: "main.js"
  },
  plugins: [
    new webpack.NoErrorsPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  module: {
      loaders: [
          { test: /\.js$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/ },
          { test: /\.jsx?$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/ },
          { test: /\.svg$/, loader: "raw" },
          { test: /\.css$/, loader: "style-loader!css-loader" },
          { test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=src/css/[name].[ext]'}

      ]
  }
};

在我的CSS文件中,我有以下内容:

@font-face {
  font-family: 'Darkenstone';
  src: url('./Darkenstone.woff') format('woff');
}

body {
  background-color: green;
  font-size: 24px;
  font-family: 'Darkenstone';
}

最后,在我的index.js文件中调用我的CSS文件:

import './src/css/master.css';

所有东西都运作正常,但字体从未加载。


我知道这已经过时了,但我很好奇你在编辑器中使用的文件图标主题是什么?看起来非常漂亮! - Jamie Reardon
9个回答

48

尝试了很多方法后,下一个加载器起作用了。我使用了 url-loader 而不是 file-loader ,需要先安装 url-loader。

{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' }

23
Base64内联编码并不是一个好主意,因为外部资源可以更有效地被缓存。此外,在移动设备上解释速度慢了30%,这是一个巨大的差异。 - Eru
1
在我苦苦挣扎了几个小时之后,你救了我的命! - Narges Moemenyan
6
如果你使用Webpack 5或更高版本,那么这不再是正确的答案。请查看@arbob的答案,其中包括“asset/resource”。 - trusktr

27
根据Webpack的文档,您需要更新webpack.config.js来处理字体文件。请参阅最后一个加载器,即:
{
   test: /\.(woff|woff2|eot|ttf|otf)$/i,
   type: 'asset/resource',
  }

我们将为webpack包含各种文件格式。最终的webpack.config.js文件应该长这样。
const path = require('path');
module.exports = {
       entry: './src/index.js',
       output: {
         filename: 'bundle.js',
         path: path.resolve(__dirname, 'dist'),
       },
       module: {
         rules: [
           {
             test: /\.css$/i,
             use: ['style-loader', 'css-loader'],
           },
           {
             test: /\.(png|svg|jpg|jpeg|gif)$/i,
             type: 'asset/resource',
           },
          {
            test: /\.(woff|woff2|eot|ttf|otf)$/i,
            type: 'asset/resource',
          },
         ],
       },
     };

然后,你需要将字体导入到入口文件中。在这个例子中是./src/index.js。配置好加载器并放置好字体后,你可以通过@font-face声明来引入它们。本地的url(...)指令会像图片一样被webpack捕获。


请仅提供有用的回答,即使它回答了问题,也不要只提供链接。请将链接中相关部分添加到您的回答中。 - Sfili_81
4
这是截至Webpack 5的正确答案。任何涉及loader的其他内容都已过时。file-loader已被弃用:https://v4.webpack.js.org/loaders/file-loader/ - trusktr

19

使用webpack 4,以下操作解决了我的问题(diff):

       {
         test: /\.svg$/,
         use: ['svg-loader']
       },
       {
         test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
         use: ['file-loader']
       }
       { test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: ['url-loader?limit=100000'] }

为了使用url-loader,我不得不移除svg-loaderfile-loader

我的app.scss文件如下:

$fa-font-path: '~font-awesome/fonts';
@import '~font-awesome/scss/font-awesome';

$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
@import '~bootstrap-sass/assets/stylesheets/bootstrap';

然后在我的app.js中,我导入了app.scss

import './app.scss';

所以,在这些更改之后,我的Webpack配置看起来像这样:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpackConfig = require('./webpack.config');

module.exports = {
  entry: {
    app: './client/app/app.js'
  },
  devtool: 'source-map',
  mode: 'development',
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Development',
      template: 'client/index.html'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader',
        ],
      },
      { test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: ['url-loader?limit=100000'] }
    ]
  }
};

更新:编译RuleSet失败:'loader'上的查询参数已被移除,改为使用'options'属性。因此只需要进行更改。 - Eggcellentos

7

我在从Webpack4.x.x更新到最新的5.52.1版本时遇到了一些字体方面的问题。

我的导出.woff / .woff2 / .ttf ...文件的内容看起来像这样:

export default __webpack_public_path__ + "glyphicons-halflings-regular.woff2";

对我来说解决方案就是完全删除之前的file-loader。似乎文件被处理了多次,导致了错误。

之前的代码:

...
module: {
    rules: [
        {
            test: /\.(png|jpg|gif|ico|woff|woff2|ttf|svg|eot)$/,
            use: {
                loader: 'file-loader',
                options: { 
                    name: '[name].[ext]',
                    useRelativePath: true
                }
            }
        },
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: { url: false }
                }
            ]
        }
    ]
}
...

之后:

...
module: {
    rules: [
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                {
                    loader: 'css-loader',
                    options: { url: false }
                }
            ]
        }
    ]
}
...

更多信息请访问:https://webpack.js.org/guides/asset-modules/


1
这个对我非常有帮助,当我尝试在 Next.js 项目中添加自定义字体加载时。谢谢! - rgoldfinger

4

我花了一点时间才弄明白,所以我在这里发布,以防将来有人需要帮助。 如果您正在使用常规 css 在一个 node js / webpack 构建中,那么这个解决方案对我有效:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
        options: {
          presets: ["@babel/preset-env"],
        },
      },
    },
    {
      test: /\.html$/,
      exclude: /node_modules/,
      use: "html-loader",
    },
    {
      test: /\.(png|svg|jpg|jpeg|gif)$/i,
      type: "asset/resource",
      generator: {
        filename: "assets/[name][ext][query]",
      },
    },
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: "asset/resource",
      generator: {
        filename: "fonts/[name][ext][query]",
      },
    },
  ],
},

在你的 .js 文件中添加所需的导入语句:
// images references in the manifest
import "../../assets/icon-16.png";
import "../../assets/icon-32.png";
import "../../assets/icon-64.png";
import "../../assets/icon-80.png";
import "../../assets/icon-25.png";
import "../../assets/icon-48.png";
//fonts referenced in the css
import "../../Fonts/opensans-regular-webfont.eot";
import "../../Fonts/opensans-regular-webfont.svg";
import "../../Fonts/opensans-regular-webfont.ttf";
import "../../Fonts/opensans-regular-webfont.woff";
import "../../Fonts/opensans-regular-webfont.woff2";
import "../../Fonts/OpenSans-Regular.ttf";

接下来,您需要更新您的.css文件以指向创建的dist/fonts文件:

  @font-face {
  font-family: 'open_sansregular';
  src: url('fonts/opensans-regular-webfont.eot');
  src: url('fonts/Fonts/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/opensans-regular-webfont.woff2') format('woff2'), url('fonts/opensans-regular-webfont.woff') format('woff'), url('fonts/opensans-regular-webfont.ttf') format('truetype'), url('fonts/opensans-regular-webfont.svg#open_sansregular') format('svg');
  font-weight: normal;
  font-style: normal;
}

html,
body {
    font-family: open_sansregular !important
}

接着构建项目。


4

我遇到了同样的问题。在我的情况下,字体的 'src' 变成了 src="[object Module]"。

禁用 webpack 上的 esModule 是解决方案:

{
  test: /\.(png|jpe?g|gif|svg|ttf|woff|otf)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        name: '[name].[contenthash].[ext]',
        outputPath: 'static/img',
        esModule: false // <- here
      }
    }
  ]
}

关于这方面的更多信息,请点击此处


那对我很有用,但我使用的是 url-loader,而不是 file-loader。我正在使用 webpack 4.43.0。 - Dex

2
在webpack.config.ts中,我需要两件事情: 使用以下代码,Webpack将处理字体文件。
module: {
  rules: [
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: 'asset/resource',
    }
  ]
}

接着,我需要用css-loader来让webpack理解@font-face和url(),并使用style-loader将CSS注入到DOM中。

module: {
  rules: [
    {
      test: /\.(woff|woff2|eot|ttf|otf)$/i,
      type: 'asset/resource',
    },
    {
      test: /\.css$/i,
      use: [
        'style-loader',
        'css-loader'
      ]
    },
  ]
}

然后在您的 index.css 文件中添加以下内容(我的字体名为 Exo):

@font-face {
  font-family: "Exo";
  src: local(Exo), // This names it to use in css
    url('./fonts/exo-v20-latin/exo-v20-latin-regular.woff2') format('woff2'),
    url('./fonts/exo-v20-latin/exo-v20-latin-regular.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}

 html, body {
  font-family: Arial, Helvetica, sans-serif;
 }

我推荐使用woff格式,因为它们可以处理所有现代浏览器。
请确保将您的CSS文件添加到主应用程序index.js中:
import './index.css';

如果您希望构建,那么这些文件最终应该出现在您的构建/分发/公共文件夹中(无论您将您的构建文件夹命名为什么)。

npm run build

或者

yarn run build

对我很管用,希望对你也有效!


1
尝试将您的加载器更改为:
{test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file?name=[name].[ext]'}

publicPath: 'build/' 添加到你的 output 键中。

在我的输出路径(webpack.config.js)中,我有“build”,因此使用该加载器,我会在build文件夹内再次得到另一个build文件夹。类似于build/build。 - Ebenizer Pinedo
好的,我已经从加载器中删除了build/。这应该将字体放在您的构建文件夹的根目录下,对吧?然后在“output”中添加publicPath: 'build/'。您可以打开index.html并查找样式标签,看看字体的URL是什么,它应该是build/Darkenstone.woff - Esben
我在控制台中收到以下错误:95% emitError:EACCES:权限被拒绝,打开'/ Darkenstone.woff' at Error(native)我已删除Path:__dirname +“/ build”以添加publicPath,这样可以吗? - Ebenizer Pinedo
不,你应该同时拥有 path: __dirname + '/build'publicPath: 'build/'。请将 index.html 的内容附加在你的问题中。 - Esben
1
实际上,我已经通过不同的加载器解决了我的问题,答案在下面,但非常感谢你的帮助 ;) - Ebenizer Pinedo
BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffixSo, it should be:{test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file-loader?name=[name].[ext]'} - mykeels

0
我将webpack版本升级到4.42.0,从4.20.2,并且我的代码开始加载我想要的字体。
const URL_LOADER_LIMIT = 8192

module.exports = () => ({
  module: {
    rules: [
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT
            }
          }
        ]
      },
      {
        test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT,
              mimetype: 'application/font-woff'
            }
          }
        ]
      },
      {
        test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'fonts/',
              limit: URL_LOADER_LIMIT,
              mimetype: 'application/octet-stream'
            }
          }
        ]
      },
      {
        test: /\.mp3$/,
        use: ['file-loader']
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              outputPath: 'images/',
              name: '[name].[ext]',
              limit: URL_LOADER_LIMIT,
              mimetype: 'image/svg+xml'
            }
          }
        ]
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              outputPath: 'images/',
              name: '[name].[ext]',
              limit: URL_LOADER_LIMIT
            }
          }
        ]
      }
    ]
  }
})


1
此答案并未提供真正应对问题的解决方案。仅建议更新版本可能不太合适,因为您还添加了一个长配置,请同时给出解释以及在此配置中可能有何不同之处。 - Julian Kleine

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