在上一章中,学习了如何为 Next.js 应用程序添加样式。接下来,我们将继续改进主页,添加自定义字体和主图。
在本章中...
以下是我们将要介绍的主题:
- 如何使用
next/font
添加自定义字体。 - 如何使用
next/image
添加图像。 - Next.js 中如何优化字体和图像。
为什么要优化字体?
字体在网站设计中扮演了重要角色,但在项目中使用自定义字体可能会影响性能,因为需要加载和获取字体文件。
Cumulative Layout Shift, CLS 是 Google 用来评估网站性能和用户体验的一个指标。在使用字体时,当浏览器初次渲染文本时使用的是回退或系统字体,随后在自定义字体加载后进行替换,这种替换可能导致文本大小、间距或布局的变化,从而引起布局的偏移。
当你使用 next/font
模块时,Next.js 会自动优化应用中的字体。它会在构建时下载字体文件,并与其他静态资源一起托管。这意味着当用户访问你的应用程序时,不需要额外的网络请求来加载字体,从而避免性能受到影响。
是时候进行测验了!
Next.js 如何优化字体?
A. 它导致额外的网络请求,从而提高性能。 B. 它禁用所有自定义字体。 C. 它在运行时预加载所有字体。 D. 它将字体文件与其他静态资源一起托管,因此不需要额外的网络请求。
添加主要字体
让我们向你的应用程序添加一个自定义的 Google 字体,看看它是如何工作的!
在你的 /app/ui
文件夹中创建一个名为 fonts.ts
的新文件。你将使用此文件来保存应用程序中使用的字体。
从 next/font/google
模块中导入 Inter
字体——这将是你的主要字体。然后,指定要加载的 字符子集(subset),在本例中是 'latin'
:
// /app/ui/fonts.ts import { Inter } from 'next/font/google'; export const inter = Inter({ subsets: ['latin'] });
最后,在 /app/layout.tsx
中将该字体添加到 <body>
元素中:
// /app/layout.tsx import '@/app/ui/global.css'; import { inter } from '@/app/ui/fonts'; export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en"> <body className={`${inter.className} antialiased`}> {children} </body> </html> ); }
通过将 Inter
添加到 <body>
元素中,该字体将应用于整个应用程序。在此处,还添加了 Tailwind 的 antialiased 类,使字体更为平滑。虽然使用此类不是必要的,但它能增加视觉效果。
导航到浏览器,打开开发者工具并选择 body
元素。你应该会看到 Inter
和 Inter_Fallback
现在已经应用在样式中。
练习: 添加次要字体
你还可以为应用程序的特定元素添加字体。
现在轮到你开始操作了!在 fonts.ts
文件中,导入名为 Lusitana
的次要字体,并将其应用到 /app/page.tsx
文件中的 <p>
元素。除了像之前一样指定字符子集外,你还需要指定字体 **粗细(weight)**。
当你准备好后,展开以下代码段查看解决方案。
提示:
- 如果你不确定应该为字体传递哪些粗细选项,可以检查代码编辑器中的 TypeScript 错误。
- 访问 Google Fonts 网站,搜索
Lusitana
以查看可用选项。- 查看有关添加多种字体的文档以及完整的选项列表。
建议先自己动手试一试进行实现, 之后再看解决方案
解决方案
// /app/ui/fonts.ts import { Inter, Lusitana } from 'next/font/google'; export const inter = Inter({ subsets: ['latin'] }); export const lusitana = Lusitana({ weight: ['400', '700'], subsets: ['latin'], });
// /app/page.tsx import AcmeLogo from '@/app/ui/acme-logo'; import { ArrowRightIcon } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { lusitana } from '@/app/ui/fonts'; export default function Page() { return ( // ... <p className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`} > <strong>Welcome to Acme.</strong> This is the example for the{' '} <a href="https://nextjs.org/learn/" className="text-blue-500"> Next.js Learn Course </a> , brought to you by Vercel. </p> // ... ); }
最后,<AcmeLogo />
组件也使用了 Lusitana 字体。为了避免错误,该组件此前被注释掉了,现在可以取消注释:
// /app/page.tsx // ... export default function Page() { return ( <main className="flex min-h-screen flex-col p-6"> <div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52"> <AcmeLogo /> {/* ... */} </div> </main> ); }
好的,你已经为应用程序添加了两种自定义字体!接下来,让我们为主页添加一个主图。
为什么要优化图像?
Next.js 可以在顶级的 /public 文件夹下提供 静态资源 ,例如图像。/public
文件夹中的文件可以在应用程序中引用。
使用常规 HTML 时,你可以如下添加图像:
<img src="/hero.png" alt="显示桌面版本仪表板项目的屏幕截图"/>
然而,这意味着你必须手动:
- 确保图像在不同屏幕尺寸下具有响应性。
- 为不同设备指定图像尺寸。
- 防止图像加载时出现布局偏移。
- 对视口外的图像进行延迟加载。
图像优化是 Web 开发中的一个重要话题,甚至可以被视为一种专门化的领域。你可以使用 next/image
组件来自动优化图像,而不是手动实现这些优化。
<Image> 组件
<Image>
组件是 HTML <img>
标签的扩展,并附带了自动图像优化功能,如:
- 在加载图像时自动防止布局偏移。
- 调整图像大小以避免将大图像发送到视口较小的设备上。
- 默认对图像进行延迟加载(图像在进入视口时加载)。
- 当浏览器支持时,以现代格式(如 WebP 和 AVIF)提供图像。
添加桌面主图
让我们使用 <Image>
组件。如果你查看 /public
文件夹,你会看到两个图像:hero-desktop.png
和 hero-mobile.png
。这两个图像是完全不同的,它们将根据用户设备是桌面还是移动设备来显示。
在你的 /app/page.tsx
文件中,从 next/image 导入组件。然后,在注释下方添加图像:
// /app/page.tsx import AcmeLogo from '@/app/ui/acme-logo'; import { ArrowRightIcon } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { lusitana } from '@/app/ui/fonts'; import Image from 'next/image'; export default function Page() { return ( // ... <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12"> {/* 在此处添加主图 */} <Image src="/hero-desktop.png" width={1000} height={760} className="hidden md:block" alt="显示桌面版本仪表板项目的屏幕截图" /> </div> // ... ); }
在这里,你将图像的 width
设置为 1000
像素,height
设置为 760
像素。为了避免布局偏移,最好为图像设置宽度和高度,并确保这些比例与源图像的宽高比 相同。
你还会注意到 hidden
类将图像从移动屏幕的 DOM 中移除,而 md:block
类在桌
面屏幕上显示图像。
你的主页现在应该如下所示:
练习: 添加移动端主图
现在轮到你了!在刚添加的图像下方,添加另一个用于 hero-mobile.png
的 <Image>
组件。
- 该图像的
width
应为560
像素,height
为620
像素。 - 它应在移动屏幕上显示,在桌面上隐藏——你可以使用开发者工具检查桌面和移动图像是否正确切换。
当你准备好后,再看以下代码段查看解决方案。
// /app/page.tsx import AcmeLogo from '@/app/ui/acme-logo'; import { ArrowRightIcon } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { lusitana } from '@/app/ui/fonts'; import Image from 'next/image'; export default function Page() { return ( // ... <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12"> {/* Add Hero Images Here */} <Image src="/hero-desktop.png" width={1000} height={760} className="hidden md:block" alt="Screenshots of the dashboard project showing desktop version" /> <Image src="/hero-mobile.png" width={560} height={620} className="block md:hidden" alt="Screenshot of the dashboard project showing mobile version" /> </div> //... ); }
很好!你的主页现在有了自定义字体和主图。
是时候进行测验了!
测试一下刚刚学到的内容。
True or False: 没有指定尺寸的图像和 Web 字体是导致布局偏移的常见原因。
A. True B. False
推荐阅读
关于这些主题还有很多内容可以学习,包括优化远程图像和使用本地字体文件。如果你想深入了解字体和图像优化,请参阅: