在 Material-UI 自定义主题中实现自托管字体(ReactJS)?

10

目前我正在使用Material-UI为ReactJS设置我的第一个项目。由于我想使用自定义字体(托管在服务器上,而不是Google Fonts或类似的东西)来自定义默认主题,因此我开始了这项工作。

然而,在构建过程中或浏览器控制台中没有出现任何错误,但它就是无法加载。我已经尝试了StackOverflow和Material-UI repo中提出的多个解决方案,但我无法使其正常工作,因此我不知道该怎么办。

我已经尝试了其他线程中建议的几种方法,在官方Material-UI文档和Material-UI GitHub存储库的问题中也有提到,但都没有成功。

我可能忽略了一些显而易见的东西,或者混淆了不同的概念,因此非常感谢任何帮助 :)

主题定义:

import {createMuiTheme} from "@material-ui/core";

import Penna from "./../../assets/fonts/penna.otf";

const pennaFont = {
    fontFamily: 'Penna',
    fontStyle: 'normal',
    fontDisplay: 'swap',
    fontWeight: 400,
    src: `
    local('Penna'),
    url(${Penna})
  `,
}

const MUI_THEME = createMuiTheme({
    typegraphy: {
        fontFamily: ['Penna', "'Helvetica Neue'", 'Helvetica', 'Arial', 'sans-serif'].join(","),
        fontSize: "16px",
        fontWeightLight: "300",
        fontWeightRegular: "400",
        fontWeightMedium: "700",
    },
    overrides: {
        MuiCssBaseline: {
            '@global': {
                '@font-family': [pennaFont],
            },
        },
    },
});

export default MUI_THEME;

顶层入口点:

// ReactJS related
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from 'react-router-dom';

// Material-UI
import {ThemeProvider} from "@material-ui/styles";
import CssBaseline from '@material-ui/core/CssBaseline';

// Custom
import MuiTheme from "./base/MuiTheme";
import Main from "./Main";

ReactDOM.render(
    <ThemeProvider theme={MuiTheme}>
        <CssBaseline/>
        <BrowserRouter>
            <Main/>
        </BrowserRouter>
    </ThemeProvider>,
    document.getElementById("root"));

Webpack 配置:

const path = require("path");

module.exports = {
    mode: "development",
    entry: path.join(__dirname, "../..", "src", "client", "js", "index.js"),
    output: {
        path: path.join(__dirname, "../..", "dist", "js"),
        filename: "index.js"
    },
    module: {
        rules: [
            {
                exclude: path.join(__dirname, "node_modules"),
                test: /\.jsx?$/,
                use: {
                    loader: "babel-loader",
                },
            },
            {
                test: /\.scss$/,
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader",
                    options: {}
                }]
            },
            {
                test: /\.(woff(2)?|eot|ttf|otf)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'assets/fonts'
                    }
                }]
            }
        ],
    },
    resolve: {
        extensions: [".css", ".scss", ".js", ".jsx", ".json", ".otf"],
    },
    target: "web",
    context: __dirname,
    stats: {
        colors: true,
        reasons: true,
        chunks: true,
    },
};

package.json:

{
  "name": "romys-hairstyling",
  "version": "0.0.1",
  "description": "Official page for Romy's Hairstyling.",
  "main": "index.js",
  "scripts": {
    "build:react": "webpack --config ./config/webpack/webpack.config.js",
    "build:scss:base": "sass src/client/scss/base/_index.scss dist/css/base.css",
    "copy:html": "node ./scripts/copy.js ./src/client/index.html ./dist/index.html -f",
    "copy:images": "node ./scripts/copy.js ./src/client/assets/images ./dist/images -Rf",
    "watch": "npm-run-all -p watch:react watch:scss:base watch:html watch:images -l",
    "watch:react": "webpack --config ./config/webpack/webpack.config.js --watch --watch-aggregate-timeout 500 --watch-poll 1000 --info-verbosity verbose",
    "watch:scss:base": "chokidar \"./src/client/scss/base\" -c \"npm run build:scss:base\" --verbose --initial",
    "watch:html": "chokidar \"./src/client/index.html\" -c \"npm run copy:html\" --verbose --initial",
    "watch:images": "chokidar \"./src/client/assets/images\" -c \"npm run copy:images\" --verbose --initial",
    "start:server": "nodemon ./src/server/server.js",
    "test:eslint:summary": "eslint -c ./.eslintrc ./src/client/js/index.js",
    "test:eslint:fix": "eslint --fix -c ./.eslintrc ./src/client/js/index.js"
  },
  "keywords": [
    "Romy",
    "Hairstyling"
  ],
  "author": "Tomas Schlepers",
  "license": "ISC",
  "dependencies": {
    "@material-ui/core": "^3.9.3",
    "@material-ui/icons": "^3.0.2",
    "express": "^4.16.4",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^4.3.1"
  },
  "devDependencies": {
    "@babel/core": "^7.4.3",
    "@babel/preset-env": "^7.4.3",
    "@babel/preset-react": "^7.0.0",
    "@material-ui/styles": "^4.0.0",
    "babel-loader": "^8.0.5",
    "chokidar-cli": "^1.2.2",
    "css-loader": "^2.1.1",
    "eslint": "^5.16.0",
    "eslint-plugin-react": "^7.12.4",
    "file-loader": "^3.0.1",
    "node-sass": "^4.12.0",
    "nodemon": "^1.18.11",
    "normalize.css": "^8.0.1",
    "npm-run-all": "^4.1.5",
    "sass": "^1.20.1",
    "sass-loader": "^7.1.0",
    "shelljs": "^0.8.3",
    "style-loader": "^0.23.1",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.1"
  }
}

正如所说,我没有收到任何错误消息。我还可以在编译的JS代码中看到对Penna字体的引用,但是当查看渲染的DOM树时,我无处看到定义的自定义主题。


1
当您将主题中的 typegraphy 更改为 typography 时会发生什么? - Josh Wooding
尽管听起来很简单,但这并不改变它无法工作的事实。 然而,我会再次尝试之前所有的解决方案,因为我不知道这个错别字是什么时候出现的。 - Togren
这个错别字和切换回MuiThemeProvider组件而不是ThemeProvider解决了问题,非常感谢 :) - Togren
不用担心 :) 如果您正在使用 v3.9 的 ThemeProvider,则需要执行旧的引导步骤 https://v3-9-0.material-ui.com/css-in-js/basics/#migration-for-material-ui-core-users - Josh Wooding
3个回答

17

将您的字体文件添加到字体目录中(./fonts/Raleway-Regular.woff2)


以woff/woff2格式将字体导入代码。文档中说明支持ttf/woff/woff2,但对我而言,ttf并没有起作用。

https://material-ui.com/customization/typography/

import RalewayWoff2 from './fonts/Raleway-Regular.woff2';
import RalewayBoldWoff2 from './fonts/Raleway-Bold.woff2';

const raleway = {
  fontFamily: 'Raleway',
  fontStyle: 'normal',
  fontDisplay: 'swap',
  fontWeight: 400,
  src: `
    local('Raleway'),
    local('Raleway-Regular'),
    url(${RalewayWoff2}) format('woff2')
  `,
};

const ralewayBold = {
  fontFamily: 'Raleway',
  fontStyle: 'normal',
  fontDisplay: 'swap',
  fontWeight: 700,
  src: `
    local('Raleway'),
    local('Raleway-Bold'),
    url(${RalewayBoldWoff2}) format('woff2')
  `,
};

在终端中运行此命令,以在构建过程中使用可处理文件加载的加载程序。
npm install --save-dev file-loader

请参考https://jimthedev.com/2016/07/28/using-google-fonts-in-webpack-and-react/


然后在主题中定义字体

const theme = createMuiTheme({
  typography: {
    fontFamily: [
      'Raleway',
      '-apple-system',
      'BlinkMacSystemFont',
      '"Segoe UI"',
      'Roboto',
      '"Helvetica Neue"',
      'Arial',
      'sans-serif',
      '"Apple Color Emoji"',
      '"Segoe UI Emoji"',
      '"Segoe UI Symbol"',
    ].join(','),
  },
  overrides: {
    MuiCssBaseline: {
      '@global': {
        '@font-face': [raleway,ralewayBold],
      },
    },
  },
});

您可以使用像这样导入的粗体字体。 font-weight: 700;

5
我之前一直使用相同的方法,但这个答案中提到的“ttf 对我无效”的关键点也适用于我。改成了 .woff2 格式后,第一次就成功了。感谢在回答中提到这点,希望能够帮助其他人。 - Jamie Shepherd
2
感谢您提供的解决方案!为了让TTF正常工作,我只需要按照上述步骤操作,但是不同的是,我需要在typescript中添加一个font.d.ts文件,并且加入declare module '*.ttf';。此外,我还需要从import RalewayTTF from './fonts/Raleway.ttf;中导入RalewayTTF,并将以下代码添加到CSS样式表中:src: ` local('Raleway'), url(${RalewayTTF}) format('truetype') `, - kusold

9

补充上面的评论。 ttf 是可以使用的,但是你需要指定格式为 truetype。所以代码应该像这样。

import Raleway from './fonts/Raleway-Regular.tff';

const raleway = {
  fontFamily: 'Raleway',
  fontStyle: 'normal',
  fontDisplay: 'swap',
  fontWeight: 400,
  src: `
    url(${Raleway}) format('truetype')
  `,
};

1
将“ttf”更改为“truetype”最终对我起作用了。谢谢! - Adam Bohannon

1
添加unicodeRange 像这样:

const myFont = { 
        unicodeRange:
        "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF UTF-8",
    }

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