Notes
React
进阶

进阶

了解完语法之后,让我们一起在本地启动一个服务,开始 React 之旅。

安装 Node.js

React 是一个基于 JavaScript 的库,Node.js 是一个 JavaScript 运行环境,我们需要在本地安装 Node.js,才能运行 React 项目。

Node.js 的安装非常简单,只需要在官网下载安装包,然后一路下一步即可。

Node.js 官网 (opens in a new tab),安装时选择 LTS 版本即可。

安装完成后,打开命令行,输入 node -v,如果能够输出版本号,说明安装成功。

安装 NPM

NPM 不需要刻意安装,Node.js 安装完成后,NPM 也就安装完成了。NPM 作为 Node.js 的包管理工具,我们可以通过 NPM 来安装 React。

打开命令行,输入 npm -v,如果能够输出版本号,说明安装成功。

使用 React 框架

React 官方建议选择社区中流行的、由 React 驱动的框架。这些框架提供大多数应用和网站最终需要的功能,包括路由、数据获取和生成 HTML。当前,官方已经不推荐原来的 create-react-app,而是推荐使用 Next.js。接下来,我们就使用 Next.js 来创建一个 React 项目。

创建项目

打开一个空文件夹,打开 PowerShell.

创建项目

输入以下命令:

npx create-next-app

它会让你填写一些项目:

What is your project named? my-app  # 项目名称,随便填写
Would you like to use TypeScript? No / Yes  # 是否使用 TypeScript,暂时不用,填写 No
Would you like to use ESLint? No / Yes  # 是否使用 ESLint,暂时不用,填写 No
Would you like to use Tailwind CSS? No / Yes  # 是否使用 Tailwind CSS,这个填写 Yes
Would you like to use `src/` directory? No / Yes  # 是否使用 src 目录,我们这里不用,填写 No
Would you like to use App Router? (recommended) No / Yes  # 是否使用 App Router,我们这里不用,填写 No
Would you like to customize the default import alias (@/*)? No / Yes  # 是否自定义模块导入别名,我们这里不用,填写 No

创建完成后,会自动安装依赖,安装完成后,文件夹会有以下文件:

使用 VSCode 打开项目,可以看到项目的目录结构:

打开终端:

输入以下命令,启动项目:

npm run dev

在浏览器中打开 http://localhost:3000,可以看到项目已经启动成功了。

💡

npm 命令: npm 作为 Node.js 的包管理工具,也可以用于执行一些命令。因为 React 是一个框架,而浏览器是无法直接运行框架的代码的,最终需要编译为原生的 HTML、CSS 和 JavaScript,才能在浏览器中运行。所以,我们需要使用 npm 来执行一些命令,比如启动项目、编译项目等。让我们不妨打开 package.json 文件,看一下里面的内容:

package.json

这里声明的一些 scripts,就是我们可以通过 npm 来执行的命令。比如,我们可以通过 npm run dev 来启动项目,通过 npm run build 来编译项目。 当你在终端中输入 npm run dev 时,实际上执行的是 next dev 命令,这个命令是 Next.js 提供的,用于启动项目。在这背后,Next.js 会自动帮我们编译项目,然后启动一个服务器,让我们可以在浏览器中访问项目。

你可以将项目中的 pages/index.js 文件中的内容全部删除,然后修改为以下内容,然后保存,浏览器会自动刷新,你就可以看到修改后的效果了。

export default function Home() {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

你可以看到下面的效果:

export default function Home() {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  );
}

外部组件

一个页面可能由很多组件组成,合理地拆分组件,可以让代码更加清晰,便于维护。

如下图中,我们可以将页面拆分为 LOGONavBarArticleFooter 四个组件。

我们可以将组件放在 components 目录下,然后在页面中引入。

分别创建组件:

components/logo.js

export default function LOGO() {
  return (
    <div>
      <h1>Stutuer</h1>
    </div>
  );
}

注意,每个组件的名称首字母必须大写,否则会报错。

且每个单独的组件必须有一个根元素,否则也会报错。比如下面的代码就会报错:

export default function LOGO() {
  return (
    <h1>Stutuer</h1>
    <p>这是一个网站</p>
  );
}

必要要使用一个根元素包裹起来:

export default function LOGO() {
  return (
    <>
      <h1>Stutuer</h1>
      <p>这是一个网站</p>
    </>
  );
}

这里使用了一个空标签 <>,它是一个占位符,不会在页面中显示。

每个组件需要用 export default 导出,才能在其他地方引入。

components/NavBar.js

export default function NavBar() {
  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <a href="/">Home</a>
      <a href="/about">About</a>
      <a href="/contact">Contact</a>
    </div>
  );
}

components/article.js

export default function Article() {
  return (
    <div>
        <h1>Article</h1>
        <p>
            Lorem ipsum dolor sit amet consectetur adipisicing elit.
            Quisquam, voluptatum. Quisquam, voluptatum. Quisquam, voluptatum.
            Quisquam, voluptatum. Quisquam, voluptatum. Quisquam, voluptatum.
        </p>
    </div>
  );
}

components/footer.js

export default function Footer() {
  return (
    <div>
        <h1>Footer</h1>
    </div>
  );
}

然后在 pages/index.js 中引入:

import LOGO from "../components/logo";
import NavBar from "../components/NavBar";
import Article from "../components/article";
import Footer from "../components/footer";

然后就可以在页面中使用组件了:

export default function Home() {
  return (
    <div>
      <LOGO />
      <NavBar />
      <Article />
      <Footer />
    </div>
  );
}

运行项目,可以看到被四个组件组装起来的页面:

import LOGO from "../components/logo";
import Footer from "../components/footer";
import Article from "../components/article";
import NavBar from "../components/NavBar";
export default function Home() {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <LOGO />
        <NavBar />
      </div>
      <Article />
      <Footer />
    </div>
  );
}

当然,你也可以在组件中使用其他组件,比如在 NavBar 组件中使用 LOGO 组件:

import LOGO from "../components/logo";
export default function NavBar() {
  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <LOGO />
      <a href="/">Home</a>
      <a href="/about">About</a>
      <a href="/contact">Contact</a>
    </div>
  );
}

那么我们只需要在 Index 页面中引入 NavBar 组件即可,LOGO 组件会作为 NavBar 的子组件被渲染出来。

import Footer from "../components/footer";
import Article from "../components/article";
import NavBar from "../components/NavBar";
export default function Home() {
  return (
    <div>
      <NavBar />
      <Article />
      <Footer />
    </div>
  );
}

静态资源

在 Next.js 中,我们可以在 public 目录下存放静态资源,比如图片、视频、音频等。

你可以在 public 目录下创建一个 images 目录,然后将图片放在里面。比如有一张图片 logo.png,那么我们可以在页面中这样引入:

<img src="/images/logo.png" />

你可能注意到,这里的路径是 /images/logo.png,而不是 ../public/images/logo.png,这是因为 Next.js 会将 public 目录下的文件映射到根路径 / 下,所以我们可以直接使用 /images/logo.png。正因为如此,你可以在浏览器导航栏输入 http://localhost:3000/images/logo.png 来正确访问到这张图片。

路由

Next.js 采用了约定式路由,即页面的路径和文件路径是一致的。你会发现,我们在创建工程时,会自动生成 pages 目录,这个目录下的每个文件(除了 _app.js_document.js)或文件夹下的文件都会被映射为一个路由。

例如,我们在 pages 目录下创建一个 about.js 文件,那么这个文件就会被映射为 /about 路由。

import LOGO from "../components/logo";
import Footer from "../components/footer";
import NavBar from "../components/NavBar";
export default function About() {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <LOGO />
        <NavBar />
      </div>
      <div>
       This is the about page
      </div>
      <Footer />
    </div>
  );
}
import LOGO from "../components/logo";
import Footer from "../components/footer";
import NavBar from "../components/NavBar";
export default function About() {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <LOGO />
        <NavBar />
      </div>
      <div>
       This is the about page
      </div>
      <Footer />
    </div>
  );
}

我们修改一下 NavBar 组件,添加一个 About 的链接:

import LOGO from "./logo";
import Link from "next/link";
export default function NavBar() {
  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <LOGO />
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
    </div>
  );
}

注意,我们在这里使用了 Link 组件,这是 Next.js 提供的一个组件,用于实现客户端路由。由于它是一个组件,所以我们需要在 NavBar 组件中引入它。

import Link from "next/link";

然后我们就可以在 NavBar 组件中使用它了,使其包裹 a 标签,这样点击 a 标签时就会触发路由跳转。

<Link href="/about">
  <a>About</a>
</Link>
import LOGO from "./logo";
import Link from "next/link";
export default function NavBar() {
  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <LOGO />
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
    </div>
  );
}

点击 About 链接,你会发现页面跳转了到了 http://localhost:3000/about,并且页面内容也发生了变化。点击 Home 链接,页面又跳转回了 http://localhost:3000/

样式

在 Next.js 中,我们可以使用 CSS 模块化来管理样式。CSS 模块化是一种将 CSS 作用域限制在组件内部的技术,这样就可以避免全局污染,同时也可以避免组件之间的样式冲突。

例如,在这里,我们将 about.js 删除,然后创建 About 文件夹,再在里面创建 index.js 文件,并创建 index.module.css 文件。

index.js 中添加下面的内容:

import LOGO from "../../components/logo";
import Footer from "../../components/footer";
import NavBar from "../../components/NavBar";
import styles from "./index.module.css";
export default function About() {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <LOGO />
        <NavBar />
      </div>
      <div className={styles.container}>
       This is the about page
      </div>
      <Footer />
    </div>
  );
}

然后在 index.module.css 中添加下面的内容:

.container {
  font-size: 20px;
  color: white;
  background: #282c34;
  padding: 20px;
}
import LOGO from "../../components/logo";
import Footer from "../../components/footer";
import NavBar from "../../components/NavBar";
import style from "./index.module.css";
export default function About() {
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <LOGO />
        <NavBar />
      </div>
      <div className={style.container}>
       This is the new about page
      </div>
      <Footer />
    </div>
  );
}

可以看到样式已经生效了。