Next.js 14 初学者入门指南(下)

随着现代Web应用的发展,用户界面变得越来越复杂,同时用户对应用的响应速度和互动性有着更高的期待。在这样的背景下,Next.js 作为一个前沿的React框架,提供了一系列高级功能来满足开发者的需求,今天我们来介绍 Next.js 14 的第二部分。

推荐阅读

‍‍Next.js 14 初学者入门指南 (上)‍‍

Metadata:在Next.js项目中优化SEO

在当今这个信息爆炸的时代,拥有一个高可见度的网站已成为许多企业和个人的追求。搜索引擎优化(SEO)是实现这一目标的重要手段。为了让你的Next.js应用更好地被搜索引擎发现,Next.js引入了一个非常实用的功能------元数据API。通过这个API,你可以为每个页面定义元数据,确保当你的页面被分享或索引时显示准确、相关的信息。

  1. 静态元数据的配置

静态元数据是指在构建时确定的有关页面的信息,并且在运行时不会改变。这些元数据与特定页面相关联,可以包括标题和描述等数据。例如,如果你有一个关于页面,你可以这样配置它的静态元数据:

go 复制代码
//src/app/about/page.tsx

export const metadata = {
  title: "关于我",
};

export default function About() {
  return <h1>关于我</h1>;
}

通过这种方式,当你的"关于我"页面被搜索引擎索引或被分享到社交媒体时,其标题会正确地显示为"关于我"。

  1. 动态生成的元数据

与静态元数据不同,动态元数据允许你根据运行时的动态数据或条件生成页面的元数据。这对于那些内容经常变化或依赖于用户输入的页面非常有用。比如,你有一个展示产品详情的页面,可以这样配置其元数据:

go 复制代码
import { Metadata } from "next";

type Props = {
  params: {
     productId: string;
  };
};

export const generateMetadata = ({ params }: Props ): Metadata => {
  return {
      title: `产品 ${params.productId} 的详情`,
  };
};

export default function ProductDetails({ params }: Props) {
    return <h1>产品 {params.productId} 的详情</h1>;
}

甚至,你可以使用异步函数来生成元数据,这在你需要从数据库或API获取数据时特别有用:

go 复制代码
export const generateMetadata = async ({ params }: Props): Promise<Metadata> => {
    const title = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(`产品 ${params.productId}`);
      }, 100);
  });
  return { title: `产品 ${title} 的详情`, };
};

3、元数据规则

  • layout.tsx 和 page.tsx 文件都可以导出元数据。如果在布局中定义,则适用于该布局中的所有页面;如果在页面中定义,则仅适用于该页面。

  • 元数据按顺序读取,从根级别到最终页面级别。

  • 如果同一路由的多个位置有元数据,它们会被合并,但页面元数据会替换布局元数据(如果它们有相同的属性)。

4、title metadata

关于元数据中的title属性,这是一个非常关键的部分,它直接影响到你的页面在搜索引擎中的显示标题以及用户在浏览器标签页中看到的内容。title可以是一个字符串或者是一个对象,这取决于你想如何控制标题的显示。

当你在layout.tsx文件中定义元数据时,title字段提供了几个有趣的选项来增加灵活性:

go 复制代码
//layout.tsx

export const metadata: Metadata = {
  title: {
    absolute: "",
    default: "",
    template: "%s | 网站名称",
  },
  description: "",
};
  • absolute:这是一个备选项,用于设置一个绝对的标题,通常在这里不会设置值。

  • default:这个值将作为默认标题,即如果子路由没有指定自己的标题,那么就会使用这个默认值。

  • template:这是一个模板字符串,子路由的标题名将替换%s。这样,你可以很容易地为站点中的每个页面添加一个统一的后缀或前缀,比如网站名称或者是一个关键的标识符。

这个功能特别适合那些页面结构复杂、需要精细控制每个页面标题的网站。通过在不同级别(全局布局、页面布局、单独页面)精心设计title的设置,可以确保无论用户进入网站的哪个部分,都能通过标题快速了解内容,并通过模板确保网站的整体品牌一致性得到维护。

举个例子,如果你的一个页面没有指定特定的标题,那么它就会使用default中的值。而当页面指定了自己的标题时,template中定义的模式就会发挥作用,自动将页面的标题和网站名称进行组合,形成一个既清晰又具有品牌特色的标题展示。

这种灵活性和自动化的结合,不仅使得SEO优化变得简单,而且还能在提升用户体验的同时,加强网站品牌的影响力。

通过精心设计每个页面的元数据,不仅可以提高网站的搜索引擎排名,还能提升用户体验,增加点击率。在社交媒体时代,一个吸引人的页面标题和描述可以大大增加内容的分享率。而Next.js提供的元数据API,让这一切变得简单而直接。

在构建一个动态且互动性强的网站时,页面间的导航是不可或缺的一环。Next.js 为此提供了非常便利的解决方案------Link 组件和 useRouter 钩子,让客户端导航变得既简单又高效。

Link 组件是 Next.js 中用于实现路由跳转的主要方式,它基于 HTML 的 <a> 元素进行了扩展,使得在 Next.js 应用中的路由之间进行导航变得非常简便。使用 Link 组件时,你只需要导入它并指定 href 属性为目标路径即可:

go 复制代码
import Link from "next/link";

<Link href="/blog">博客</Link>

三、Navigation:程序化导航

有时候,我们需要在代码中根据某些条件或逻辑来动态导航到不同的页面,这时就可以使用 Next.js 提供的 useRouter 钩子。useRouter 允许你访问路由对象,通过这个对象,你可以控制应用的路由行为,例如进行页面跳转。

以下是一个使用 useRouter 进行程序化导航的示例:

go 复制代码
import { useRouter } from "next/router";

const MyComponent = () => {
  const router = useRouter();

  const handleClick = () => {
    console.log("下单");
    router.push("/"); // 使用 router.push 方法跳转到首页
  };

  return (
    <button onClick={handleClick}>下单</button>
  );
}

在这个例子中,当用户点击"下单"按钮时,handleClick 函数会被触发,然后应用会使用 router.push("/") 代码来跳转到首页。这种方式非常适合在用户完成某些操作后需要自动跳转页面的场景。

无论是通过 Link 组件还是 useRouter 钩子进行导航,Next.js 都为开发者提供了极大的便利和灵活性。通过这些工具,你可以轻松地在你的应用中实现复杂的导航逻辑,为用户提供流畅且富有互动性的网页体验。

四、Templates(模板)

在构建现代Web应用时,开发者常常需要在多个页面之间共享某些布局或样式。Next.js的模板(Templates)功能就是为此而生。模板类似于布局(Layouts),它们都可以包裹每个子布局或页面。但模板和布局在功能上有一定的差异,特别是在处理页面导航时。

模板的特性

当用户在共享同一模板的不同路由之间导航时,模板会呈现一些独特的行为:

  • 重新挂载组件:每次导航到新路由时,即使新旧路由共享相同的模板,该模板的一个新实例也会被挂载。

  • DOM元素重建:模板中的DOM元素会在每次导航时被重新创建,而不是复用。

  • 状态不保留:由于模板重新挂载,任何在模板中维持的状态都将丢失,每次导航都是从新的状态开始。

  • 效果重新同步:React的效果(effects)会在每次导航时重新同步,意味着例如useEffect中的代码会在每次模板挂载时执行。

定义模板

定义模板非常简单,你只需要创建一个默认导出的React组件,这个组件可以从template.js或template.tsx文件中导出。这个组件通常会接受children作为其属性,并在其内部渲染这些子元素:

go 复制代码
export default function Template({ children }: { children: React.ReactNode }) {
  return <div>{children}</div>
}

模板使用场景

模板特别适合于那些需要在多个页面之间共享相同布局,但又希望在每次页面跳转时能够完全重置状态和DOM的场景。这可以确保用户在不同页面间导航时,能够获得一致且干净的体验,而不必担心前一个页面的状态影响到当前页面。

通过明智地使用模板,你可以在保持代码组织和复用性的同时,为用户提供流畅且一致的浏览体验。

五、loading.tsx

loading.tsx 文件在 Next.js 应用中扮演着特别的角色,它允许开发者为特定路由段创建加载状态,这些加载状态在内容加载时展示给用户。使用 loading.tsx 可以有效地提升用户体验,特别是在网络环境较差或内容较多需要较长时间加载的场景下。

创建加载状态

在 loading.tsx 文件中,你可以定义一个或多个加载状态的 React 组件。这些组件可以是简单的动画,如旋转的加载指示器,或者更复杂的占位符布局,如骨架屏。

go 复制代码
// loading.tsx

export default function Loading() {
    return (
        <div className="loading-container">
            <p>内容加载中,请稍候...</p>
            {/* 这里可以添加加载动画或图标 */}
        </div>
    );
}

使用加载状态

当用户导航到一个新的路由段,而这个路由段的内容还在加载时,你定义的加载状态会立即显示给用户。这提供了一个视觉反馈,让用户知道应用正在响应其操作,并且内容正在积极加载中。这样可以避免用户在看到空白页面时感到困惑或者认为应用出现了问题。

提升用户体验

利用 loading.tsx 实现的加载状态可以大大提升应用的用户体验:

  • 减少等待感:通过立即提供反馈,用户感知到的等待时间会减少,即使实际加载时间没有变短。

  • 增强应用感知速度:快速响应用户操作的应用给人的感觉更快,即使是在加载较重的内容时也不例外。

  • 保持用户参与:加载状态可以是创造性的,提供额外的视觉元素或信息,保持用户的参与度,避免他们在加载过程中离开。

在设计加载状态时,重要的是要保持它的简洁和与应用整体风格的一致性。加载状态不仅是一种功能性需求,也是提升品牌体验和应用专业度的机会。

六、error.tsx

在构建现代web应用时,有效地管理和响应错误是至关重要的。Next.js 通过文件系统层次结构中的 error.tsx 文件,为开发者提供了一种灵活而强大的方式来创建和管理错误UI,以及处理特定路由段的错误。

创建针对性的错误UI

通过在应用的不同路由级别添加 error.tsx 文件,你可以为这些路由定制特定的错误处理UI。这种方法使得在用户遇到错误时,能够展示更具体、更友好的错误消息和恢复选项,而不是一个通用的错误页面。

go 复制代码
// 使用 'use client' 来指明这些错误组件必须是客户端组件

'use client';

import { useEffect } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // 将错误记录到错误报告服务中
    console.error(error);
  }, [error]);

  return (
    <div>
      <h2>出错了!</h2>
      <button onClick={() => reset()}>
        重试
      </button>
    </div>
  );
}

隔离错误影响

将 error.tsx 文件放置于应用的不同级别,能够帮助你更精确地控制错误的影响范围。例如,在特定的路由段出现错误时,只有那部分内容会展示错误信息,应用的其他部分仍然可以正常工作。这样既提高了应用的鲁棒性,也优化了用户体验。

错误恢复功能

在 error.tsx 中,你可以提供恢复功能,如重试按钮,允许用户尝试从错误中恢复,而无需重新加载整个页面。这种快速响应错误并尝试恢复的能力,对于保持应用的交互性和用户满意度至关重要。

嵌套路由中的错误处理

通过在嵌套的文件夹结构中不同级别放置 error.tsx 文件,你可以实现更细粒度的错误处理。这意味着,你可以为应用中的不同部分定制不同的错误处理策略和UI,使错误处理更加灵活和用户友好。

这种方法利用了Next.js的文件系统路由和组件模型,提供了一种既简洁又强大的错误处理机制,帮助开发者构建更加可靠和用户友好的应用。

七、Parallel Routes(插槽)

Next.js 的并行路由是一种高级路由机制,允许在同一布局中同时渲染多个页面,极大地增强了页面布局和内容管理的灵活性。通过使用名为"插槽(slots)"的功能,开发者可以以模块化的方式组织内容。

定义插槽

要定义一个插槽,我们使用 @folder 命名约定。然后,每个插槽作为属性传递给其对应的 layout.tsx 文件。

以仪表盘为例,你可以使用并行路由同时渲染用户、收入和通知页面:

go 复制代码
// dashboard/layout.tsx

export default function DashboardLayout({
    children,
    users,
    revenue,
    notifications
}: {
    children: React.ReactNode;
    users: React.ReactNode;
    revenue: React.ReactNode;
    notifications: React.ReactNode;
}) {
    return (
      <>
        <div>{children}</div>
        <div>{users}</div>
        <div>{revenue}</div>
        <div>{notifications}</div>
      </>
    );
}

并行路由的一个优势是它们能够将单个布局划分为各种插槽,使代码更易于管理。

独立的路由处理

布局的每个插槽,例如用户分析或收入指标,都可以有自己的加载和错误状态。在不同页面部分以不同速度加载或遇到独特错误的场景中,这种细粒度的控制尤其有益。

路由内的子导航

你的仪表盘的每个插槽都可以实质上作为一个小应用程序运行,完备自己的导航和状态管理。这在诸如仪表盘这样的复杂应用中特别有用,不同部分服务于不同的目的。

go 复制代码
//dashboard/@notifications/page.tsx

export default function Notifications() {
  return (
    <div>通知</div>
    <Link href="/dashboard/">归档</Link>
  );
};

//dashboard/@notifications/archieved/page.tsx

export default function ArchivedNotifications() {
  return (
    <div>归档通知</div>
    <Link href="/dashboard/">默认</Link>
  );
};

这种结构不仅提升了代码的模块化和可读性,而且还增强了用户界面的交互性,使用户能够在仪表盘的不同部分之间流畅地导航,同时各部分能够独立地加载和处理数据。这样的设计思想,为构建复杂且高效的Web应用提供了新的可能性。

结束

通过今天的分享,我们了解了Next.js并行路由的强大之处,以及它如何使我们能够构建更加动态和响应式的Web应用。这项技术不仅提高了应用的性能和用户体验,还让代码的组织和维护变得更加高效。

在这信息爆炸的时代,我始终致力于探索前端技术的最新动态,为你带来更多的技术干货。如果你对今天的内容感兴趣,不妨点赞、关注、转发,并在评论区留下你的看法和问题。我非常期待你的反馈和互动!

此外,不要忘了关注「前端达人」,我将不定期分享更多关于前端开发的技术文章、教程和最佳实践。让我们一起在前端的道路上不断进步,探索更多可能性!

相关推荐
程序员_三木10 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊20 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
tianmu_sama26 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全29 分钟前
Java的基础概念(一)
java·开发语言·python
liwulin050630 分钟前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc34 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Oneforlove_twoforjob39 分钟前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
羚羊角uou41 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++