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, 界面如下:
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>
);
}
添加后,界面如下:
Tailwind
Tailwind是一个CSS框架,它允许你直接在TSX标记着快速编写实体类,从而加快了开发过程。
在Tailwind中,你可以通过添加类名来设置元素的样式。例如,添加类“text-blue-500”将使<h1>文本变为蓝色。
<h1 className='text-blue-500'>我是蓝色</h1>
如果你更喜欢编写传统的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用于在桌面屏幕上显示图像。
现在主页的样子:
添加移动端添加Hero图片
使用另一个<Image>组件展示移动端图片hero-mobile.png。
这个图片设置宽560,高620像素,它应该移动屏幕上显示,在桌面屏幕上隐藏。
<Image
src="/hero-mobile.png"
alt="Hero Image"
width={560}
height={320}
className="block md:hidden"
/>
移动屏幕页面显示如下:
4. 创建布局和页面
嵌套路由
Next.js使用文件系统路由,其中文件夹用来常见嵌套路由。每个文件夹代表一个映射到URL段的路由段。
你可以使用layout.tsx和page.tsx文件为每条路线创建单独的UI。
page.tsx是一个特殊的Next.js文件,用于导出React组件,这是路由可访问所必需的。
要创建嵌套路由,你可以将文件夹相互嵌套,并在其中添加page.tsx文件。比如:
/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 文件夹下的所有页面都会自动嵌套在 中,如下所示:
现在的页面呈现如下:
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