跳转到内容

组件和页面

想象一下,我们现在正在制作一个提示条,我们希望实现这样的一个通知框:

标题

内容

这里给出这个组件的 HTML 代码:

<div
style="
width: 100%;
border: 1px dotted #aaa;
border-radius: 5px;
padding: 10px;
position: relative;
background-color: #f9f9f9;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
"
>
<h4
style="
margin: 0;
padding-bottom: 5px;
font-size: 16px;
color: #333;
border-bottom: 1px solid #ddd;
"
>
标题
</h4>
<p
style="
margin: 10px 0 0;
font-size: 14px;
color: #555;
"
>
内容
</p>
</div>

为什么需要组件?

现在,想象一下,你需要做一个通知中心,这个通知中心会有很多这样的通知条,你会怎么做呢?你会复制粘贴这个 HTML 代码很多次吗?这样做的话,你会发现,当你需要修改这个通知条的样式时,你需要修改很多地方,这样会很麻烦。

怎么创建一个组件?

因此,我们有了 组件 的概念。组件就是一个可以重复使用的代码块。在 React 中,我们可以使用 函数组件 来定义一个组件,就和定义任何函数或者变量一样简单!这是一个简单的 React 组件:

const Notice = () => {
return (
<div>这是一个组件</div>
)
}

可以看到,这个组件就是一个函数,这个函数返回了一个 JSX 元素。接下来,我们就可以直接通过 <Notice /> 来使用这个组件了!

在项目中创建组件

让我们在之前的项目中实际动手试一试吧!首先,在 src 目录下创建一个 components 文件夹,然后在这个文件夹中创建一个 Notice.tsx 文件用于存放我们的通知组件。注意:在 React 中,组件的文件名必须以大写字母开头,这是为了和 HTML 标签区分开来。

然后,打开这个文件,我们可以写入以下代码:

import React from 'react'
const Notice = () => {
return (
<div>Notice</div>
)
}
export default Notice

如果你使用的是 VSCode,你也可以在文件中输入 rafce 然后按下回车,VSCode 会自动帮你生成一个函数组件。首先,让我们删除第一行 import React from 'react',因为我们并没有使用 React 这个变量。然后,我们替换掉 Notice 函数中的 div 元素,替换成我们刚刚展示的代码。现在,我们的 Notice.tsx 文件应该是这样的:

const Notice = () => {
return (
<div>这是一个组件</div>
)
}
export default Notice

使用组件

接下来,让我们使用这个组件。首先,我们需要介绍两个重要的知识和技巧。
在 React 中,每个组件只能返回一个元素。这是什么意思呢?让我们看看这段错误示范:

const Notice = () => {
return (
<div>这是一个组件</div>
<div>这是另一个组件</div> // 这里会报错
)
}

但是,我们可以使用一个 div 元素包裹这两个 div 元素,这样就不会报错了:

const Notice = () => {
return (
<div>
<div>这是一个组件</div>
<div>这是另一个组件</div>
</div>
)
}

有没有更简单的方法?当然!在 React 中,我们可以使用空标签 <></> 来包裹多个元素,这样就不需要额外的 div 元素了:

const Notice = () => {
return (
<>
<div>这是一个组件</div>
<div>这是另一个组件</div>
</>
)
}

现在,让我们在 main.tsx 文件中使用这个组件。首先,我们需要把之前的 Hello World 包裹在一个空标签中,然后在这个空标签中加入我们的 Notice 组件,这样,我们就不会因为返回多个元素而报错了:

import { createRoot } from 'react-dom/client'
import './index.css'
import Notice from './components/Notice'
createRoot(document.getElementById('root')!).render(
<>
<Notice />
<p>
Hello world!
</p>
</>
,
)
点击查看答案

在现代 IDE 中,编辑器会自动帮你补全 import 语句,所以你只需要输入 Notice 然后按下回车键,编辑器就会自动帮你补全 import Notice from './components/Notice' 这一行。

现在,你就完成了第一个组件的制作!接下来,让我们填入一些内容。

插入模板

在这篇教程开始之前,我们给出了通知组件的 HTML 代码,现在,让我们把这个代码放入我们的组件中。注意,在 React 中,我们的 HTML 代码部分语法会有一些变动,包括需要添加 '' 或者使用数组而不是 ;。观察对比在课程开始前我给出的 HTML 代码和下面的 JSXReact 中的 HTML 被我们称之为 JSX) 代码:

<div
style={{
width: '100%',
border: '1px dotted #aaa',
borderRadius: '5px',
padding: '10px',
position: 'relative',
backgroundColor: '#f9f9f9',
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)',
}}
>
<h4
style={{
margin: 0,
paddingBottom: '5px',
fontSize: '16px',
color: '#333',
borderBottom: '1px solid #ddd',
}}
>
标题
</h4>
<p
style={{
margin: '10px 0 0',
fontSize: '14px',
color: '#555',
}}
>
内容
</p>
</div>

仔细观察他们之间的区别,不必担心,在你多写几次之后,你就会习惯这种写法的。现在,让我们把这段代码放入我们的组件中:

const Notice = () => {
return (
// 上面的代码
)
}

现在,让我们刷新网页,你就应该能够看到一个通知条了!

标题

内容

传递参数

很酷吧!但是还记得我们为什么要创建组件吗?我们希望这个组件可以重复使用,而不是每次都需要手动修改内容。现在,让我们把这个组件变得更加灵活,让我们可以传入参数来改变这个通知条的内容。

首先,我们需要定义一个 interface 来规定这个组件的参数。在之前的课程中,我们有介绍过 interfacetype 的区别,在定义组件参数时,我们通常使用 interface
我们的通知条有两个参数,一个是 title,一个是 content,我们可以这样定义这个 interface 并放在 Notice.tsx 文件的上方:

interface Props {
title: string;
content: string;
}

对于组件参数,因为其他文件不会访问,因此,我们可以直接命名为 Props,当然,你也可以使用更加具体的名字,比如 NoticeProps。注意,interface type组件 的名称通常以大写字母开头。

之后,我们就可以要求这个组件接收这个参数,让我们替换 () 部分:

const Notice = (props: Props) => {
// props 是变量名称 Props 是我们定义的接口名称
return (
// ...
)
}

如何在组件中使用这个参数呢?在 JSX 语法中,我们可以使用 {} 来插入变量,这个变量可以是任何 JS 表达式。在这里,我们可以用 {props.title} 来插入 title 参数,用 {props.content} 来插入 content 参数。现在,我们的组件应该是这样的:

interface Props {
title: string;
content: string;
}
const Notice = (props: Props) => {
return (
<div style={{...}} >
<h4 style={{...}} >
{props.title}
</h4>
<p style={{...}} >
{props.content}
</p>
</div>
);
};
export default Notice
点击查看答案

就这样,我们完成了一个可以接收参数的组件!我们可以修改 main.tsx 文件来传入参数:

<Notice title="这是一个标题" content="这是一个内容" />

页面

也许你注意到了,在标题中还有 和页面 部分,其实在 React 中,组件和页面是一样的,都是由组件构成的。在下一节,我们将学习 路由 的概念和使用,之后就可以开始制作我们的页面了!