MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Next.js中字体文件的引用与管理方法

2023-10-117.9k 阅读

在 Next.js 项目中引入字体文件的基础方式

在 Next.js 项目里,引入字体文件的常见做法是借助 CSS。我们先创建一个 styles 目录,在其中新建 globals.css 文件。假设我们有一个自定义字体文件,比如 MyFont.woff2,将它放置在项目的 public 目录下。

globals.css 中,通过 @font - face 规则来定义字体:

@font - face {
  font - family: 'MyFont';
  src: url('/MyFont.woff2') format('woff2'),
       url('/MyFont.woff') format('woff');
  font - display: swap;
}

这里 font - family 定义了字体名称,src 里指定了字体文件的路径及格式。font - display: swap 确保在字体加载时,文本能快速显示,然后字体替换上去,提升用户体验。

接着,在 pages/_app.js 中导入这个 globals.css

import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

这样,整个项目都可以使用 MyFont 字体了。比如在某个组件中:

import React from'react';

const MyComponent = () => {
  return (
    <div style={{ fontFamily: 'MyFont' }}>
      This text uses the custom font.
    </div>
  );
};

export default MyComponent;

使用 CSS - in - JS 引入字体

styled - components 库的使用

如果项目使用 styled - components,我们也能引入字体。先安装 styled - components

npm install styled - components

假设同样有 MyFont.woff2 字体文件在 public 目录下。在 styles 目录创建 FontStyles.js 文件:

import { createGlobalStyle } from'styled - components';

const FontStyles = createGlobalStyle`
  @font - face {
    font - family: 'MyFont';
    src: url('/MyFont.woff2') format('woff2'),
         url('/MyFont.woff') format('woff');
    font - display: swap;
  }
`;

export default FontStyles;

然后在 pages/_app.js 中导入:

import React from'react';
import FontStyles from '../styles/FontStyles';
import { ThemeProvider } from'styled - components';

const theme = {
  // 定义主题相关内容
};

function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider theme={theme}>
      <FontStyles />
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

export default MyApp;

在组件中使用:

import React from'react';
import styled from'styled - components';

const StyledText = styled.p`
  font - family: 'MyFont';
`;

const MyComponent = () => {
  return (
    <StyledText>
      This text uses the custom font with styled - components.
    </StyledText>
  );
};

export default MyComponent;

emotion 库的使用

emotion 库同样支持 CSS - in - JS 方式引入字体。安装 @emotion/react@emotion/styled

npm install @emotion/react @emotion/styled

styles 目录创建 EmotionFont.js

import { css } from '@emotion/react';

const fontStyles = css`
  @font - face {
    font - family: 'MyFont';
    src: url('/MyFont.woff2') format('woff2'),
         url('/MyFont.woff') format('woff');
    font - display: swap;
  }
`;

export default fontStyles;

pages/_app.js 导入:

import React from'react';
import fontStyles from '../styles/EmotionFont';
import { Global } from '@emotion/react';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Global styles={fontStyles} />
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

组件中应用:

import React from'react';
import { css } from '@emotion/react';

const textStyles = css`
  font - family: 'MyFont';
`;

const MyComponent = () => {
  return (
    <p css={textStyles}>
      This text uses the custom font with emotion.
    </p>
  );
};

export default MyComponent;

从 CDN 引入字体

很多时候,我们可以借助 CDN(内容分发网络)来引入字体,这能提升加载速度,尤其是对于一些常用字体。以 Google Fonts 为例,它提供了丰富的字体资源。

pages/_app.js 中,通过 next/head 组件引入 Google Fonts:

import React from'react';
import Head from 'next/head';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin />
        <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet" />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

这里先通过 preconnect 提示浏览器预先建立连接,提升加载效率。然后引入 Roboto 字体的样式表。在组件中使用:

import React from'react';

const MyComponent = () => {
  return (
    <div style={{ fontFamily: 'Roboto, sans - serif' }}>
      This text uses the Roboto font from Google Fonts.
    </div>
  );
};

export default MyComponent;

另一个常见的 CDN 是 Font Awesome,它主要提供图标字体。假设要引入 Font Awesome 字体图标,先在 pages/_app.js 引入:

import React from'react';
import Head from 'next/head';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Head>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font - awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossOrigin="anonymous" referrerPolicy="no - referrer" />
      </Head>
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

在组件中使用图标字体:

import React from'react';

const MyComponent = () => {
  return (
    <div>
      <i className="fas fa - coffee"></i>
      This is a coffee icon from Font Awesome.
    </div>
  );
};

export default MyComponent;

字体管理与优化

字体子集化

字体子集化是优化字体加载的重要手段。当我们使用自定义字体时,可能字体文件包含了大量字符,而实际项目中可能只需要其中一部分字符。例如,我们的项目仅使用英文字符和部分标点,那么可以通过工具生成只包含这些字符的字体子集。

有多种工具可用于字体子集化,比如 fonttools。首先安装 fonttools

npm install fonttools

假设我们有一个 MyFont.woff2 字体文件,并且有一个包含项目所需字符的文本文件 subset.txt,内容如下:

A - Za - z0 - 9.,!?'

运行以下命令生成字体子集:

fonttools subset MyFont.woff2 --text-file=subset.txt --output-file=MyFontSubset.woff2

然后在项目中引用这个子集化后的字体:

@font - face {
  font - family: 'MyFont';
  src: url('/MyFontSubset.woff2') format('woff2');
  font - display: swap;
}

这样生成的字体子集文件会小很多,从而加快加载速度。

字体加载策略

除了 font - display: swap,还有其他字体加载策略可供选择,如 blockfallbackoptional

font - display: block:字体加载期间,文本不可见,字体加载完成后显示。这可能会导致文本闪烁,但能保证文本以正确字体显示。

@font - face {
  font - family: 'MyFont';
  src: url('/MyFont.woff2') format('woff2');
  font - display: block;
}

font - display: fallback:字体加载时,先显示系统默认字体,在较短时间(通常 100ms)内字体加载完成则替换,否则继续使用默认字体。

@font - face {
  font - family: 'MyFont';
  src: url('/MyFont.woff2') format('woff2');
  font - display: fallback;
}

font - display: optional:如果字体在本地缓存或能快速加载,就使用自定义字体,否则使用系统默认字体。适用于不常用的字体。

@font - face {
  font - family: 'MyFont';
  src: url('/MyFont.woff2') format('woff2');
  font - display: optional;
}

根据项目需求选择合适的字体加载策略,能在用户体验和加载性能间找到平衡。

动态字体加载

在某些场景下,可能需要动态加载字体。比如根据用户的语言偏好加载不同语言对应的字体。

我们可以通过 JavaScript 动态创建 link 元素来加载字体。在一个自定义钩子 useDynamicFont.js 中实现:

import { useEffect } from'react';

const useDynamicFont = (language) => {
  useEffect(() => {
    const link = document.createElement('link');
    link.rel ='stylesheet';
    if (language === 'zh') {
      link.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap';
    } else if (language === 'en') {
      link.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap';
    }
    document.head.appendChild(link);
    return () => {
      document.head.removeChild(link);
    };
  }, [language]);
};

export default useDynamicFont;

在组件中使用:

import React from'react';
import useDynamicFont from './useDynamicFont';

const MyComponent = () => {
  const userLanguage = 'en';// 这里假设从用户设置获取语言
  useDynamicFont(userLanguage);
  return (
    <div style={{ fontFamily: userLanguage === 'zh'? 'Noto Sans SC, sans - serif' : 'Roboto, sans - serif' }}>
      This text uses the dynamically loaded font.
    </div>
  );
};

export default MyComponent;

处理不同环境下的字体

开发环境与生产环境

在开发环境中,我们更关注字体的快速加载和调试便利性。可以使用本地字体文件,并且使用 font - display: swap 确保能快速看到字体效果。

而在生产环境中,除了字体子集化和合适的加载策略,还可以考虑对字体文件进行压缩。常见的压缩工具如 gzipBrotli。大多数服务器都支持对静态资源进行压缩。以 gzip 为例,在 Next.js 项目中,如果使用 next - export 导出静态文件,可以在服务器配置中开启 gzip 压缩。

对于 Node.js 服务器,使用 compression 中间件:

npm install compression

在服务器入口文件(如 server.js)中:

const express = require('express');
const compression = require('compression');
const next = require('next');

const dev = process.env.NODE_ENV!== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();
  server.use(compression());

  server.all('*', (req, res) => {
    return handle(req, res);
  });

  const port = process.env.PORT || 3000;
  server.listen(port, (err) => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});

这样,字体文件在传输过程中会被压缩,减小传输大小,提升加载速度。

移动端与桌面端

移动端和桌面端的网络环境和设备性能不同,因此字体管理也有差异。在移动端,网络带宽可能有限,设备存储也相对较小。所以更应注重字体文件的大小优化,优先选择字体子集化和从 CDN 引入常用字体。

例如,对于移动端,可以优先选择 Google Fonts 中一些轻量级字体,并且只加载所需字符子集。同时,根据移动端设备的高像素密度,可能需要调整字体的显示大小和粗细,以保证可读性。

在 CSS 中,可以使用媒体查询来针对移动端和桌面端设置不同的字体样式:

/* 桌面端 */
@media (min - width: 768px) {
  body {
    font - family: 'Roboto, sans - serif';
    font - size: 16px;
  }
}

/* 移动端 */
@media (max - width: 767px) {
  body {
    font - family: 'Roboto Condensed, sans - serif';// 更轻量级字体
    font - size: 14px;
  }
}

另外,在移动端还需注意字体的触摸目标大小。如果字体过小,用户点击包含字体的按钮或链接时可能误操作。可以通过设置适当的 paddingline - height 来增大触摸目标区域。

字体与响应式设计

响应式字体大小

在响应式设计中,字体大小需要根据屏幕尺寸进行调整。可以使用相对单位如 emrem 来实现。em 单位是相对于父元素字体大小,而 rem 是相对于根元素(通常是 html 元素)字体大小。

假设在 globals.css 中设置根字体大小:

html {
  font - size: 16px;
}

在组件中使用 rem 单位设置字体大小:

import React from'react';

const MyComponent = () => {
  return (
    <div style={{ fontSize: '1.2rem' }}>
      This text has a font size relative to the root font size.
    </div>
  );
};

export default MyComponent;

当屏幕尺寸变化时,可以通过媒体查询改变根字体大小,从而实现整体字体大小的响应式调整:

/* 大屏幕 */
@media (min - width: 1200px) {
  html {
    font - size: 18px;
  }
}

/* 中等屏幕 */
@media (min - width: 768px) and (max - width: 1199px) {
  html {
    font - size: 16px;
  }
}

/* 小屏幕 */
@media (max - width: 767px) {
  html {
    font - size: 14px;
  }
}

响应式字体样式

除了字体大小,字体样式如粗细、风格也可以根据屏幕尺寸调整。比如在大屏幕上使用更粗的字体来突出标题,而在小屏幕上适当减小字体粗细以节省空间。

/* 大屏幕标题 */
@media (min - width: 1200px) {
  h1 {
    font - family: 'Roboto, sans - serif';
    font - size: 3rem;
    font - weight: 700;
  }
}

/* 小屏幕标题 */
@media (max - width: 767px) {
  h1 {
    font - family: 'Roboto Condensed, sans - serif';
    font - size: 2rem;
    font - weight: 500;
  }
}

通过合理设置响应式字体样式,能在不同设备上提供一致且良好的用户阅读体验。

字体文件格式兼容性

不同浏览器对字体文件格式的支持存在差异。常见的字体文件格式有 woff2woffttfotfeot

woff2 是最新的格式,具有最佳的压缩率,现代浏览器都广泛支持。woffwoff2 的前身,也有较好的兼容性。ttf(TrueType Font)和 otf(OpenType Font)是传统字体格式,兼容性也不错,但文件通常较大。eot(Embedded OpenType)主要用于旧版 Internet Explorer。

为了确保最大程度的兼容性,在 @font - face 规则中可以列出多种字体格式:

@font - face {
  font - family: 'MyFont';
  src: url('/MyFont.woff2') format('woff2'),
       url('/MyFont.woff') format('woff'),
       url('/MyFont.ttf') format('truetype'),
       url('/MyFont.otf') format('opentype'),
       url('/MyFont.eot');
  src: url('/MyFont.eot?#iefix') format('embedded - opentype');
  font - display: swap;
}

这样,浏览器会根据自身支持情况选择合适的字体格式加载。虽然这会增加项目资源文件的大小,但能保证在各种浏览器上都能正确显示字体。在实际项目中,可以根据目标用户群体和浏览器使用情况,合理选择需要支持的字体格式,以平衡兼容性和文件大小。例如,如果项目主要面向现代浏览器用户,可以优先使用 woff2woff 格式,减少对 eot 等旧格式的支持,从而减小项目资源体积。

字体版权与合规性

在使用字体时,必须注意字体的版权问题。许多字体有特定的使用许可协议,有些字体是免费用于个人项目但商业使用需要授权,有些则完全不允许用于商业用途。

对于从 Google Fonts 等平台获取的字体,它们通常基于开源许可协议,可免费用于商业和非商业项目。但对于一些自定义字体或从其他渠道获取的字体,务必仔细阅读其使用条款。

如果不确定字体的版权情况,建议联系字体作者或版权所有者获取明确的授权。使用未经授权的字体可能会导致法律纠纷,给项目带来不必要的风险。在项目开发过程中,可以建立一个文档记录所使用字体的来源、版权信息及许可协议,以便在项目审查或出现问题时能快速查阅。

同时,在项目上线前,再次确认所有使用字体的合规性,确保项目在法律层面上的安全性。对于大型商业项目,可能需要咨询专业的法律顾问,以确保字体使用完全符合相关法律法规。

字体与无障碍设计

字体可读性与对比度

在无障碍设计中,字体的可读性至关重要。首先要保证字体大小足够,对于正文内容,一般在桌面端不小于 16px,在移动端不小于 14px。同时,要注意字体与背景之间的对比度。

可以使用在线工具如 WebAIM 的对比度检查器(https://webaim.org/resources/contrastchecker/)来验证对比度是否符合无障碍标准。例如,对于正常文本,前景与背景的对比度至少要达到 4.5:1,对于大文本(如标题,通常定义为 18pt 及以上或 14pt 及以上且加粗),对比度至少要达到 3:1。

在 CSS 中,通过合理选择字体颜色和背景颜色来满足对比度要求:

body {
  font - family: 'Roboto, sans - serif';
  font - size: 16px;
  color: #333;
  background - color: #fff;
}

h1 {
  font - size: 24px;
  color: #000;
  background - color: #f0f0f0;
}

字体样式与特殊需求

对于有视觉障碍的用户,某些字体样式可能更易于阅读。例如,使用无衬线字体(如 Arial、Roboto 等)通常比衬线字体(如 Times New Roman)更易读,尤其是对于视力不佳或阅读困难的用户。同时,避免使用过于花哨或装饰性强的字体,这些字体可能会干扰文本识别。

对于色盲用户,要注意颜色搭配。如果通过颜色区分文本(如链接颜色),也要通过其他方式(如下划线)进行区分,以确保色盲用户也能识别。

在设计按钮或交互元素的字体时,要保证触摸目标区域足够大,方便肢体残疾用户操作。可以通过增加 paddingline - height 来增大触摸目标区域,例如:

button {
  font - family: 'Roboto, sans - serif';
  font - size: 16px;
  padding: 10px 20px;
  line - height: 1.5;
}

通过考虑这些无障碍设计因素,能使项目更具包容性,服务更广泛的用户群体。