当前位置: 首页 > article >正文

React 与 Next.js

先说说 React 与 Next.js 结合的作用:高效构建高性能与搜索引擎优化(SEO)的网页

一. React = 网站的“积木”

React 用于构建网站中的各个组件,像是“积木”一样组成页面元素(如按钮、图片、表单等)。这些组件组合在一起形成完整的页面,并允许用户互动。

例子:例如,点击按钮或填写表单等操作都可以通过 React 实现。

二. Next.js = 提供后台支持与加速

Next.js 是一个基于 React 的框架,提供服务器端渲染(SSR)、静态生成(SSG)和增量静态再生(ISR)等功能,提升性能和 SEO。

  • SSR(服务器端渲染):在服务器渲染 React 组件,用户首次访问时更快加载,提升 SEO。
  • SSG(静态生成):在构建时生成静态页面,减少服务器负担,加载速度更快。
  • ISR(增量静态再生): 允许页面在设定的时间间隔后自动更新,确保内容的时效性,同时保持静态页面的高性能和快速加载。

三. React + Next.js 的结合优势

  • 加载更快:SSR 和 SSG 提前生成页面,用户访问时即时加载。 例子:电商网站的产品页面,Next.js 生成好静态内容,搜索引擎可以直接抓取,提升排名。
  • 提升 SEO:通过 SSR 和 SSG,搜索引擎更容易读取页面内容,帮助网站更好地排名。 例子:博客网站,文章页面使用静态生成,用户访问时立刻加载。
  • 性能更好:SSG 和 ISR 提升页面加载速度,适合内容固定的页面。 例子:新闻网站的首页可以生成静态内容,而评论部分则动态生成。
  • 灵活性:根据不同页面需求选择渲染方式,优化性能与用户体验。

在开始使用 VSCode 进行 React 和 Next.js 开发之前,理解一些基础的概念和工具会更快上手。以下是一些可以先了解的内容:

1. HTML、CSS 和 JavaScript

  • HTML:了解如何结构化网页内容。
  • CSS:理解如何设计页面的外观和样式。
  • JavaScript:React 和 Next.js 都是基于 JavaScript 的,熟悉 JavaScript 的基础,特别是 ES6+ 特性(如箭头函数、解构赋值、类、模块化等)非常重要。

2. React 基础

  • 组件(Components):React 是基于组件的架构,理解函数组件和类组件的区别。
  • JSX(JavaScript XML):React 使用 JSX 来在 JavaScript 中书写 HTML。
  • 状态(State)与属性(Props):了解如何使用 state 来存储和管理组件的状态,以及通过 props 来传递数据。
  • 事件处理:React 中如何处理用户输入和交互事件(如点击、键盘输入等)。
  • 生命周期(Lifecycle):了解 React 组件生命周期,虽然在函数组件中,使用钩子(Hooks)来管理生命周期。
  • React Hooks:如 useStateuseEffectuseContext 等,现代 React 开发推荐使用函数组件和 Hooks。

3. Next.js 基础

3.1 SSR(服务器端渲染)

SSR 即服务器端渲染,意味着当用户请求一个页面时,服务器会先处理这个页面的内容,生成 HTML 后返回给浏览器。也就是说,页面的内容是在服务器上渲染完成的,而不是在浏览器上加载后再渲染。

Next.js 默认支持 SSR。在访问一个页面时,Next.js 会在服务器端生成该页面的 HTML,并把它发送到浏览器,这样浏览器能更快地显示页面内容。

3.2 SSG(静态站点生成)和 ISR(增量静态再生)

SSG 即静态站点生成,意思是页面在构建时就已经生成好 HTML,保存到磁盘上。当用户请求这些页面时,服务器会直接返回已生成的 HTML 文件。这样做的好处是非常快速,因为 HTML 已经准备好了。

  • getStaticProps:这是一个用来在构建时生成页面的函数。它允许你在构建时就把数据拉取过来,并生成静态页面。
  • getStaticPaths:用于生成动态路由页面的静态内容,配合 getStaticProps 使用。

ISR 即增量静态再生,它允许你在页面已经生成后,动态地更新某些静态页面,而不需要重新构建整个网站。你可以设置一个再生时间,当页面过期后,Next.js 会重新生成这个页面,而不是每次都重新构建所有页面。

3.3 API 路由

API 路由允许你在 Next.js 中创建服务器端的 API 端点,而无需额外的后端框架。你可以直接在 pages/api 目录下创建一个文件,每个文件就代表一个 API 路由。

  • 比如,你可以在 pages/api/hello.js 创建一个 API 路由,用户请求 /api/hello 时,Next.js 会返回该文件中定义的内容。

3.4 动态路由

动态路由指的是 URL 中某些部分可以变化,比如一个博客文章的 ID。你可以通过在文件名中使用方括号来定义动态路径。

  • 如果有一个动态路径,比如 /posts/123,可以在 pages/posts/[id].js 中创建这个页面。[id] 就是动态部分,表示 URL 中的 123,可以是任何值。
pages/
  posts/
    [id].js     // 这个页面能匹配任何类似 /posts/1 或 /posts/abc 的 URL

3.5 文件系统路由

Next.js 使用文件系统路由管理机制,也就是说,文件和文件夹的结构决定了页面的路由。你不需要手动配置路由,Next.js 会自动根据文件夹和文件名生成相应的 URL 路径。

  • 比如,pages/index.js,它的路径就是 /
  • pages/about.js,它的路径就是 /about
  • 通过这种文件夹结构,Next.js 自动生成了所有的路由。

4. React 和 Next.js 的调试

  • VSCode 的调试工具:学习如何在 VSCode 中进行调试,设置断点,检查变量,查看调用堆栈。
  • React 开发者工具:安装 React Developer Tools 扩展,能够在浏览器中检查 React 组件树和状态。
  • Next.js 的开发模式:了解 next dev 启动开发模式,能够热重载代码并显示错误信息。

5. 包管理器

React 和 Next.js 项目的依赖包通常使用 npmbun 来管理。了解如何安装、更新和删除依赖包是非常重要的。

使用 npm 或 bun 管理依赖包

  • 安装依赖包:使用 npm install 或 bun add
  • 更新依赖包:使用 npm update 或 bun upgrade
  • 删除依赖包:使用 npm uninstall <package-name> 或 bun remove <package-name>

初始化项目

创建一个新的 Next.js 项目:

# 使用 npm 创建
npx create-next-app@latest

# 使用 bun 创建
bun create next-app@latest

bun 可以通过 npm 进行安装,确保有安装 Node.js 环境,然后可以执行以下命令:

npm install -g bun

这将全局安装 bun,之后就可以使用 bun 命令来管理项目和依赖包。

安装完成后,可以通过以下命令验证 bun 是否安装成功:

bun --version

如果正确安装,命令会返回 bun 的版本号。

6. Node.js 和 npm/yarn

React 和 Next.js 都依赖于 Node.js 环境,因此了解如何安装和配置 Node.js 是非常重要的。

Node.js 环境

  • 安装 Node.js:你可以从 Node.js 官方网站 下载并安装最新版本的 Node.js。
  • 版本管理:使用 nvm(Node Version Manager)来管理多个 Node.js 版本。

其他安装问题(可以看这个,网上找到的,挺详细):

【Node.js】2025最新Windows环境下Node.js安装及环境配置保姆级教程-腾讯云开发者社区-腾讯云

遇到权限或其他问题:请查看nodejs安装目录的权限:属性-安全-编辑Users-勾选完全控制

npm/bun 命令

熟悉常用的 npmbun 命令是日常开发中的一部分。常见的命令包括:

  • 安装依赖包

    npm install <package-name>     # 使用 npm
    bun add <package-name>         # 使用 bun
  • 启动开发环境

    npm run dev                    # 使用 npm 启动开发环境
    bun dev                        # 使用 bun 启动开发环境
  • 安装所有依赖包

    npm install                    # 使用 npm 安装所有依赖包
    bun install                    # 使用 bun 安装所有依赖包
  • 运行项目构建

    npm run build                  # 使用 npm 构建项目
    bun build                      # 使用 bun 构建项目
  • 卸载依赖包

    npm uninstall <package-name>   # 使用 npm 卸载依赖包
    bun remove <package-name>      # 使用 bun 卸载依赖包

7. 开发工具与插件

  • VSCode 插件

    • ESLint:检查代码中的潜在错误和不规范的地方,确保代码质量,特别是 React 和 Next.js 项目。

    • Prettier:自动格式化代码,保证代码风格一致,特别适用于团队开发。

    • ES7 React/Redux Snippets:快速生成常用的 React 和 Redux 代码模板,提升开发效率。

    • Tailwind CSS IntelliSense:提供 Tailwind CSS 类名自动补全和提示,帮助更快构建界面。

    • GitLens:增强 Git 集成功能,方便查看历史记录和文件更改,适合团队协作。

    • Path Intellisense:自动补全文件路径,减少输入错误,适合复杂项目。

    • Auto Rename Tag:自动同步修改 HTML 和 JSX 标签,减少手动修改的错误。

    • Prisma:帮助数据库操作,适合全栈开发,但不一定适用于所有项目。

    • Markdown Preview Enhanced:预览和编辑 Markdown 文件,适合有文档需求的项目。

8. 了解项目结构

1. pages 目录

pages 目录是 Next.js 的核心部分,它用来管理项目里的所有页面。每个文件都会自动变成一个网页,Next.js 会根据文件名生成对应的路由。

核心要点:

  • 每个页面对应一个文件pages 里的每个文件会自动成为一个网页。例如:

    • pages/index.js → 主页 /
    • pages/about.js → 关于页面 /about
  • 动态路由:可以在文件名中使用方括号来表示动态路由(类似 URL 中的参数)。比如:

    • pages/blog/[id].js → /blog/1 或 /blog/abc,这里的 [id] 就是动态部分,代表不同的内容。
  • 嵌套路由:可以在 pages 里创建文件夹来组织页面。例如:

    • pages/blog/index.js → /blog
    • pages/blog/[id].js → /blog/:id
  • 特殊文件

    • _app.js:所有页面的根组件,用来设置全局布局、样式或其他全局功能。
    • _document.js:用于修改 HTML 的结构,比如 <head> 标签。
    • _error.js:自定义错误页面(比如 404 页面)。

示例:

pages/
  index.js        // 对应根路由 /
  about.js        // 对应路由 /about
  blog/
    index.js      // 对应路由 /blog
    [id].js       // 对应动态路由 /blog/:id

2. public 目录

public 目录是用来存放静态文件的地方,比如图片、字体、图标等。Next.js 会把 public 目录里的文件直接暴露出来,用户可以直接通过 URL 访问。

核心要点:

  • 静态资源:可以把图片、logo、图标等文件放在 public 文件夹里,直接通过 /logo.png 这样的路径来访问。

示例:

public/
  logo.png       // 可以通过 /logo.png 访问
  favicon.ico    // 用于设置网页的 favicon

3. styles 目录

styles 目录用来存放 CSS 文件,帮助你给页面添加样式。Next.js 支持两种样式方式:全局样式和模块化样式。

核心要点:

  • 全局样式:比如 styles/global.css,会影响整个应用。
  • 模块化样式:你可以给每个组件单独写样式,避免样式冲突。Next.js 会自动为你加上作用域,使它只影响当前组件。

示例:

styles/
  globals.css       // 全局样式
  Home.module.css   // CSS Modules,局部样式

4. components 目录

components 目录存放可复用的组件。组件是页面中的一些小模块,比如按钮、导航栏、表单等。把这些小模块提取出来,可以让代码更整洁,更容易复用。

核心要点:

  • 可复用组件:你可以把常用的部分(如按钮、头部、尾部)做成组件,然后在不同的页面中使用它们。

示例:

components/
  Header.js     // 页眉组件
  Footer.js     // 页脚组件
  Button.js     // 按钮组件

5. 其他常见目录和文件

  • libutils 目录:存放一些工具函数或外部库封装的文件,不涉及页面的 UI 部分。

    lib/
      api.js        // API 请求相关函数
      utils.js      // 工具函数
  • hooks 目录:存放自定义的 Hook。自定义 Hook 让你能够提取页面中的逻辑,保持代码清晰。

    hooks/
      useAuth.js    // 自定义 Hook,处理身份验证
  • middleware 目录:存放用于处理请求的中间件,比如身份验证。

总结:

Next.js 项目的结构大致是这样:

  • pages:这个文件夹里的每个文件都是一个页面,自动生成路由。
  • public:存放静态资源,文件可以通过 URL 直接访问。
  • styles:存放样式文件,支持全局和模块化样式。
  • components:存放可复用的 UI 组件,帮助组织页面。

通过这种结构,Next.js 让项目开发变得更简单,路由和页面自动关联,静态资源也非常容易管理。

9. 常见问题解决方案

学会查找文档和解决常见的错误或问题,Next.js 和 React 的官方文档非常详细,是解决问题的好资源。

  • React 官方文档:快速入门 – React 中文文档
  • Next.js 官方文档:快速入门 – React 中文文档

通过掌握这些基本知识,你将能够更顺利地开始在 VSCode 中进行 React 和 Next.js 开发。如果你已经对这些概念有所了解,可以直接着手开始写项目,逐步深入学习,就无需往下看了。

详细补充:

1.3 ES6+ 特性中最常用的

内容过多,HTML和CSS就不赘述了,应该都学过,就算忘了一看到代码也能回忆起来。学过XHTML更好。

1.3.1 箭头函数 (Arrow Functions)

箭头函数提供了一种更简洁的函数声明方式,并且不绑定 this。这是它最重要的特点,因为在普通函数中,this 的值是由调用该函数的方式决定的,而箭头函数的 this 是静态绑定的。

传统函数:

function greet(name) {
  return "Hello, " + name;
}

箭头函数:

const greet = (name) => {
  return "Hello, " + name;
};

如果函数体只有一行语句,可以省略 {}return 关键字:

const greet = (name) => "Hello, " + name;

1.3.2 解构赋值 (Destructuring Assignment)

解构赋值允许从数组或对象中提取值并将其赋值给变量,代码更加简洁。

数组解构:

const arr = [1, 2, 3];
const [a, b, c] = arr;  // a = 1, b = 2, c = 3

对象解构:

const person = { name: "Alice", age: 25 };
const { name, age } = person;  // name = "Alice", age = 25

你还可以重命名解构出来的变量:

const person = { name: "Alice", age: 25 };
const { name: fullName, age: years } = person;  // fullName = "Alice", years = 25

1.3.3 类 (Classes)

类是 ES6 引入的一个新特性,用于创建对象和处理继承关系,简化了之前的构造函数和原型链的使用方式。

定义类:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

const person1 = new Person("Alice", 25);
person1.greet();  // Output: Hello, my name is Alice.

类可以继承其他类,使用 extends 关键字:

class Employee extends Person {
  constructor(name, age, jobTitle) {
    super(name, age);  // 调用父类的构造函数
    this.jobTitle = jobTitle;
  }

  work() {
    console.log(`${this.name} is working as a ${this.jobTitle}.`);
  }
}

const emp1 = new Employee("Bob", 30, "Engineer");
emp1.greet();  // Output: Hello, my name is Bob.
emp1.work();   // Output: Bob is working as a Engineer.

1.3.4 模板字符串 (Template Literals)

模板字符串使得字符串拼接更加简洁且易读,尤其是包含变量时。

普通字符串拼接:

const name = "Alice";
const age = 25;
const greeting = "My name is " + name + " and I am " + age + " years old.";

模板字符串:

const name = "Alice";
const age = 25;
const greeting = `My name is ${name} and I am ${age} years old.`;

1.3.5 模块化 (Modules)

ES6 引入了模块化机制,使用 importexport 来导入和导出模块,从而使得代码更加模块化,便于组织和重用。

导出(export

// file1.js
export const greet = (name) => `Hello, ${name}!`;

导入(import

// file2.js
import { greet } from './file1';

console.log(greet('Alice'));  // Output: Hello, Alice!

如果你要导出一个默认值(比如函数或类),可以使用 export default

// file1.js
export default function greet(name) {
  return `Hello, ${name}!`;
}

然后在其他文件中导入:

// file2.js
import greet from './file1';

console.log(greet('Alice'));  // Output: Hello, Alice!

1.3.6 异步编程 (Async Programming)

ES6 引入了 Promise,之后通过 asyncawait 使得处理异步操作变得更为简洁。

Promise 示例:

const fetchData = new Promise((resolve, reject) => {
  const success = true;
  if (success) {
    resolve("Data fetched successfully!");
  } else {
    reject("Data fetching failed!");
  }
});

fetchData.then(data => console.log(data)).catch(error => console.log(error));

Async/Await 示例:

async function fetchData() {
  try {
    const data = await someAsyncFunction();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

1.3.7 扩展运算符 (Spread Operator) 和 剩余参数 (Rest Parameters)

扩展运算符 ... 用于展开数组或对象,剩余参数用于将多个参数收集到一个数组中。

扩展运算符(数组):

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];  // arr2 = [1, 2, 3, 4, 5]

扩展运算符(对象):

const obj1 = { name: "Alice", age: 25 };
const obj2 = { ...obj1, job: "Engineer" };  // obj2 = { name: "Alice", age: 25, job: "Engineer" }

剩余参数:

const sum = (...numbers) => numbers.reduce((total, num) => total + num, 0);
console.log(sum(1, 2, 3, 4));  // Output: 10

1.3.8 Promise 链式调用

在处理多个异步操作时,then()catch() 方法允许我们处理每个异步操作的结果或错误。

fetch('some-api-url')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

2.React 中的基本概念和常见的 Hooks

2.1 组件(Components)

React 是基于组件的架构,所有的 UI 都是由组件构成的。组件可以是函数组件类组件

函数组件(Functional Component)

函数组件是最常用的一种方式,使用函数来定义组件,并通过返回 JSX 来渲染界面。

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

export default Greeting;

类组件(Class Component)

类组件是 React 较早期的做法,它继承自 React.Component 类,并通过 render() 方法返回 JSX。

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

export default Greeting;

2.2 JSX(JavaScript XML)

JSX 是一种 JavaScript 的语法扩展,允许我们在 JavaScript 中书写类似 HTML 的代码。React 使用 JSX 来定义组件的 UI。JSX 让编写 React 组件的界面部分变得非常直观和简洁。

JSX 示例:

const element = <h1>Hello, world!</h1>;

特点:

  • JSX 会被转换为 JavaScript 代码。例如,<h1>Hello</h1> 会变成 React.createElement('h1', null, 'Hello')
  • JSX 可以嵌入表达式,使用 {} 包裹:
const name = "Alice";
const element = <h1>Hello, {name}!</h1>;

2.3 状态(State)与属性(Props)

状态(State)

状态用于存储和管理组件的数据,通常在函数组件中通过 useState Hook 来管理。在类组件中,使用 this.state 来定义和更新状态。

函数组件中的状态:
import React, { useState } from 'react';

function Counter() {
  // 使用 useState 创建一个状态变量 count,初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;
类组件中的状态:
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

export default Counter;

属性(Props)

属性是从父组件传递到子组件的数据。父组件通过 props 向子组件传递数据,子组件通过 props 来访问这些数据。

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return <Welcome name="Alice" />;
}

export default App;

2.4 事件处理

在 React 中,事件处理方式与传统的 JavaScript 略有不同。事件名采用驼峰命名法(例如:onClickonChange),并且事件处理函数需要传入作为属性的函数。

事件处理示例:

import React, { useState } from 'react';

function ClickCounter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

export default ClickCounter;
  • 事件处理函数如 handleClick 会在事件触发时调用。
  • 在 JSX 中,onClick={handleClick} 将 handleClick 作为事件处理函数传递。

2.5 生命周期(Lifecycle)

React 组件有一个生命周期,指的是从组件创建到销毁的整个过程。在函数组件中,生命周期钩子是通过 Hooks 来管理的。类组件中则有一些生命周期方法,比如 componentDidMountcomponentWillUnmount 等。

函数组件中的生命周期 (使用 useEffect Hook)

useEffect 用来模拟类组件中的生命周期方法,例如 componentDidMountcomponentDidUpdatecomponentWillUnmount

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // 模拟 componentDidMount 和 componentDidUpdate
    const interval = setInterval(() => {
      setSeconds((prevSeconds) => prevSeconds + 1);
    }, 1000);

    // 模拟 componentWillUnmount
    return () => clearInterval(interval);
  }, []); // 空数组表示只在组件挂载和卸载时执行

  return <div>Time: {seconds} seconds</div>;
}

export default Timer;
  • useEffect 第一个参数是副作用函数,在组件挂载后执行。
  • 第二个参数是依赖数组,如果数组为空,则只在挂载和卸载时执行副作用。

类组件中的生命周期方法

类组件有一些生命周期方法,用于不同的生命周期阶段:

  • componentDidMount:组件挂载后调用。
  • componentDidUpdate:组件更新后调用。
  • componentWillUnmount:组件卸载前调用。
class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState({ seconds: this.state.seconds + 1 });
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return <div>Time: {this.state.seconds} seconds</div>;
  }
}

export default Timer;

2.6 React Hooks

React Hooks 是 React 16.8 引入的特性,用于在函数组件中管理状态、生命周期等。

useState

useState 用于在函数组件中管理状态。

const [state, setState] = useState(initialValue);

useEffect

useEffect 用于处理副作用(如数据获取、订阅、定时器等)。可以模拟生命周期方法。

useEffect(() => {
  // 执行副作用
  return () => {
    // 清理副作用
  };
}, [dependencies]);

useContext

useContext 用于访问上下文(Context)中的值。Context 提供了一种方式让我们在组件树中传递数据,而不需要显式地通过每个组件的 props

const value = useContext(MyContext);

http://www.kler.cn/a/536270.html

相关文章:

  • DeepSeek LLM(初代)阅读报告
  • 集合类不安全问题
  • π0开源了且推出自回归版π0-FAST——打造机器人动作专用的高效Tokenizer:比扩散π0的训练速度快5倍但效果相当
  • SpringBoot的工作原理
  • RabbitMQ的安装
  • IDEA使用Auto-dev+DeepSeek 10分钟快速集成,让java开发起飞
  • D. CGCDSSQ
  • 十三、Dockerfile 常用镜像创建
  • RabbitMQ业务场景面试题
  • yum 安装mysql
  • VDN 微服务架构搭建篇(三)基于 Nacos 的 Spring Cloud Gateway 动态路由管理
  • 如何使用iframe来渲染ThingsBoard仪表盘
  • LabVIEW与PLC交互
  • 【Spring】什么是Spring?
  • SAP物料账未分配差异-采购发票数量大于库存数量
  • 多无人机--强化学习
  • 20.责任链模式(Chain of Responsibility Pattern)
  • 搜索+图论1 练习答案+思路
  • 蓝桥算法基础2
  • EtherCAT帧捕获与帧结构分析
  • 基于Bootstrap + Java + Oracle实现的电商平台
  • DeepSeek图解10页PDF
  • STM32自学记录(八)
  • 【ArcGIS Pro 简介1】
  • Docker Desktop安装kubernetes时一直在Starting:Kubernetes failed to start
  • Day56_20250204_图论part1_图论理论基础|深搜理论基础|98.所有可达路径|广搜理论基础