学习Next.js (https://nextjs.org/learn)章节1~5

110 阅读7分钟

1. 开始

创建一个新项目

打开你的终端,cd到你想要创建项目的目录,执行下面的命令

npx create-next-app@latest nextjs-dashboard --example "https://github.com/vercel/next-learn/tree/main/dashboard/starter-example" --use-pnpm

模拟数据

  • 可以使用JSON格式或者Js对象来模拟数据
  • 可以使用第三方平台,比如mockAPI

在这个项目中,在app/lib/placeholder-data.ts文件中已经提供了一些模拟数据。

开发环境运行项目

运行 pnpm i 安装项目需要的包

pnpm i

接着执行 pnpm dev 启动开发服务

访问http://localhost:3000, 界面如下:

image.png

2. CSS 样式

全局样式

在 /app/ui 文件夹下,有一个global.css文件,你可以在这个文件中添加CSS规则,添加的规则会在所有页面生效。

可以在项目中的任意组件中引入global.css文件,但通常在根组件上引入。

在 /app/layout.tsx文件中引入global.css文件

import '@/app/ui/global.css'; 

export default function RootLayout({ 
    children,
   }: { 
    children: React.ReactNode;
   }) { return ( 
       <html lang="en"> 
           <body>{children}</body> 
        </html> 
       );
    }

添加后,界面如下:

image.png

Tailwind

Tailwind是一个CSS框架,它允许你直接在TSX标记着快速编写实体类,从而加快了开发过程。

在Tailwind中,你可以通过添加类名来设置元素的样式。例如,添加类“text-blue-500”将使<h1>文本变为蓝色。

     <h1 className='text-blue-500'>我是蓝色</h1>

image.png

如果你更喜欢编写传统的CSS规则或将样式与JSX分开,CSS模块是一个很好的选择。

CSS 模块

CSS 模块允许你通过自动创建唯一的类名将CSS范围限定到组件,由此你不必担心样式冲突

使用clsx库切换类名

在某些情况下,你可能需要根据状态或其他条件设置元素的样式。

clsx是一个允许你轻松切换类名的库。

import clsx from 'clsx';

export default function InvoiceStatus({ status }: { status: string }) {
  return (
    <span
      className={clsx(
        'inline-flex items-center rounded-full px-2 py-1 text-xs',
        {
          'bg-gray-100 text-gray-500': status === 'pending',
          'bg-green-500 text-white': status === 'paid',
        },
      )}
    >
      // ...
    </span>
  );
}

其他的样式

  • Sass可以导入.css和.scss文件
  • CSS-in-JS库,比如styled-jsx, styled-component,emotion

3. 优化文本和图片

为什么要优化字体

字体在网站设计中起着重要作用,但如果需要获取和加载字体文件,在项目中使用自定义字体可能会影响性能。

累积布局偏移是谷歌用来评估网站性能和用户体验的指标。对于字体,当浏览器最初以回退或系统字体呈现文本,然后在加载后将其替换为自定义字体时,就会发生布局转换。这种交换可能会导致文本大小、间距或布局发生变化,从而移动周围的元素。

当您使用Next/font模块时,Next.js会自动优化应用程序中的字体。它在构建时下载字体文件,并将其与其他静态资源一起托管。这意味着当用户访问您的应用程序时,不会有影响性能的额外字体网络请求。

添加自定义字体

在项目中添加一个自定义谷歌字体,看看它是如何工作的。

在/app/ui文件夹中,创建一个fonts.ts文件。

从 next/font/google模块中导入Inter字体

import { Inter } from 'next/font/google';

export const inter = Inter({ subsets: ['latin']})

最后,在 /app/layout.tsx文件中的body元素上添加字体

<html lang="en">
      <body className={ `${inter.className} antialiased` }>{children}</body>
    </html>

为什么要优化图片

Next.js在根目录下的 /public文件下存放静态资源,比如图片。

传统HTML,你可能会像下面这样添加图片

<img 
    src="/hero.png"
    alt=""
/>

像上面这样添加图片,你需要手动:

  • 确保你的图像在不同的屏幕尺寸上都有响应
  • 为不同设备指定图像大小
  • 手动防止加载图片时发生布局位移
  • 手动延迟加载用户视口外的图像

图片优化是web开发中的一个大主题,其本身可以被视为一种专业化。你可以使用Next/image组件自动优化图像,而不是手动实现这些优化。

<Image>组件

<Image>组件是HTML<img>标签的扩展,并带有自动图像优化功能,例如:

  • 防止加载图像时自动发生布局偏移。
  • 调整图像大小,以避免将大图像传送到具有较小视口的设备。
  • 默认情况下,延迟加载图像(图像在进入视口时加载)。
  • 在浏览器支持的情况下,以WebP和AVIF等现代格式提供图像。

在桌面端添加Hero图片

让我们使用组件。如果你查看/public文件夹,你会看到两个图像:hero-desktop.png和hero-mobile.png。这两个图像完全不同,它们将根据用户的设备是台式机还是移动设备而显示。 在/app/page.tsx文件中,从next/image导入组件。然后,在注释下添加图像:

    import Image from 'next/image';
    
    {/* Add Hero Images Here */}
     <Image
            src="/hero-desktop.png"
            alt="Hero Image"
            width={1000}
            height={760}
            className="hidden md:block"
          />

将宽度设置为1000,高度设置为760像素。设置图像的宽度和高度以避免布局偏移的很好的做法,这些图像的纵横比应该与源图像相同。

隐藏类用于从移动屏幕上的DOM中删除图像,以及md:block用于在桌面屏幕上显示图像。

现在主页的样子:

image.png

添加移动端添加Hero图片

使用另一个<Image>组件展示移动端图片hero-mobile.png。

这个图片设置宽560,高620像素,它应该移动屏幕上显示,在桌面屏幕上隐藏。

    <Image
            src="/hero-mobile.png"
            alt="Hero Image"
            width={560}
            height={320}
            className="block md:hidden"
          />

移动屏幕页面显示如下:

image.png

4. 创建布局和页面

嵌套路由

Next.js使用文件系统路由,其中文件夹用来常见嵌套路由。每个文件夹代表一个映射到URL段的路由段。

image.png 你可以使用layout.tsx和page.tsx文件为每条路线创建单独的UI。

page.tsx是一个特殊的Next.js文件,用于导出React组件,这是路由可访问所必需的。

要创建嵌套路由,你可以将文件夹相互嵌套,并在其中添加page.tsx文件。比如:

image.png

/app/dashboard/page.tsx 与 /dashboard路径相关联。

创建dashboard页面

在 /app 下新增一个 dashboard 文件夹,在 dashboard 文件夹里面新建一个 page.tsx 文件, 文件内容如下:

export default function Page() {
    return  <p>Dashboard Page</p>;
}

创建更多dashboard页面

在 /app/dashboard 下新增 customers 文件夹,再在customers 文件夹下新增 page.tsx 文件,文件内容如下:

export default function Page() {
    return <p>Customers Page</p>;
}

在 /app/dashboard 下新增 invoices 文件夹,再在 invoices 文件夹下新增 page.tsx 文件,文件内容如下:

export default function Page() {
    return <p>Invoices Page</p>;
}

创建dashboard布局

仪表盘具有跨多个页面共享的某种导航功能。在Next.js中,你可以使用一个特殊的Layout.tsx文件来创建多个页面之间共享的UI。

首先在/dashboard文件夹中,添加一个layout.tsx文件。文件内容如下:

 import SideNav from "@/app/ui/dashboard/sidenav";  

export default function Layout({ children} : { children: React.ReactNode }) {
  return (
    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
        // 左侧导航
        <div className="w-full flex-none md:w-64">
          <SideNav />
        </div>
        // 右侧内容
        <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
      </div>
  );
}

<Layout /> 组件中导入了一个 <SideNav />组件,你在该文件中导入的任意组件都可以作为 layout的一部分。

<Layout /> 组件接受一个 children 属性,这个 children 可以是一个页面,也可以是另一个layout。在这个案例中, /dashboard 文件夹下的所有页面都会自动嵌套在 中,如下所示:

image.png

现在的页面呈现如下:

image.png

5. 页面间导航

在页面之间跳转,传统上使用HTML的 <a />标签, 但使用<a />标签跳转,会导致整个页面刷新。

使用<Link>组件

使用<Link>组件可以实现客户端导航。

 import {
  UserGroupIcon,
  HomeIcon,
  DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
 
// ...
 
export default function NavLinks() {
  return (
    <>
      {links.map((link) => {
        const LinkIcon = link.icon;
        return (
          <Link
            key={link.name}
            href={link.href}
            className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
          >
            <LinkIcon className="w-6" />
            <p className="hidden md:block">{link.name}</p>
          </Link>
        );
      })}
    </>
  );
}

自动代码拆分和预加载

为了改善代码体验,Next.js 会自动按照路由段对应用程序进行代码拆分。这与传统的React SPA不同,在传统的SPA中,浏览器在初始加载时加载所有应用程序代码。

按路由拆分代码意味着页面变得孤立。如果某个页面抛出错误,应用程序的其余部分仍将工作。

此外,在生产环境中,每当<Link>组件出现在浏览器的视口中时,Next.js都会在后台自动预加载路由代码,用户点击链接时,目标页面的代码已经在后台加载。

英文原版教程

英文原版:nextjs.org/learn