个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展
📃个人状态: 研发工程师,现效力于中国工业软件事业
🚀人生格言: 积跬步至千里,积小流成江海
🥇推荐学习:🍍前端面试宝典 🎨100个小功能 🍉Vue2 🍋Vue3 🍓Vue2/3项目实战 🥝Node.js实战 🍒Three.js
🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
文章目录
创建 React 项目
我们可以使用 vite 构建工具创建 react 项目。
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev
访问:http://localhost:5173/ 网址,即可得到如下页面。
我的第一个组件
几个注意事项:
- React 组件是常规的 JavaScript 函数,但组件的名称必须以大写字母开头,否则它们将无法运行!
- 没有括号包裹的话,任何在 return 下一行的代码都将被忽略!
- 组件可以渲染其他组件,但是不要嵌套他们的定义,不然代码会非常慢,并且会导致 bug 产生。应该在顶层定义每个组件,当子组件需要使用父组件的数据时,需要通过 props 的形式进行传递,而不是嵌套定义。
添加 src/Profile.jsx
文件。
export default function Profile() {
return (
<>
<p>伟大的科学家</p>
<img
src="https://i.imgur.com/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
</>
)
}
把 Profile.jsx 放入 App.jsx。
import './App.css'
import Profile from './Profile'
function App() {
return (
<>
<Profile />
</>
)
}
export default App
使用 JSX 书写标签语言
JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。
规则:
- 只能返回一个根对象
- 标签必须闭合
- 使用驼峰式命名法给
所有大部分属性命名
创建 src/TodoList.jsx
文件,若不想在标签中增加一个额外的 <div>
,可以用 <>
和 </>
元素来代替。这个空标签被称作 Fragment。React Fragment 允许你将子元素分组,而不会在 HTML 结构中添加额外节点。
为什么多个 JSX 标签需要被一个父元素包裹?
答:JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。
为什么要使用驼峰命名?
答:JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在你自己的组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。
export default function TodoList() {
return (
<>
<h1>海蒂·拉玛的待办事项</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
style={{ width: '200px', borderRadius: '10px' }}
/>
<ul>
<li>发明一种新式交通信号灯</li>
<li>排练一个电影场景</li>
<li>改进频谱技术</li>
</ul>
</>
);
}
在 JSX 中通过大括号使用 JavaScript
JSX 允许你在 JavaScript 中编写类似 HTML 的标签,从而使渲染的逻辑和内容可以写在一起。
在 JSX 中使用 JavaScript 很简单,只需要使用 {}
包裹着即可。有时我们会在 JSX 中看到 {{
和 }}
,其实它只不过是 包在大括号里的一个对象 罢了。
下面我们尝试在 TodoList.jsx
中通过 {}
使用 JavaScript。
export default function TodoList() {
const person = {
name: "海蒂·拉玛",
avatar: "https://i.imgur.com/yXOvdOSs.jpg",
theme: {
backgroundColor: "orange",
color: "white",
borderRadius: "10px",
padding: "10px",
},
todolist: ["发明一种新式交通信号灯", "排练一个电影场景", "改进频谱技术"],
};
return (
<div style={person.theme}>
<h1>{person.name}的待办事项</h1>
<img
src={person.avatar}
alt="Hedy Lamarr"
className="photo"
style={{ width: "200px", borderRadius: "10px" }}
/>
<ul>
<li>{person.todolist[0]}</li>
<li>{person.todolist[1]}</li>
<li>{person.todolist[2]}</li>
</ul>
</div>
);
}
将 Props 传递给组件
React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它。
将父组件的信息传递给子组件非常的简单,只需要在父组件中定义需要传递的内容,然后在子组件中 通过参数接受 即可。
我们修改 src/Profile.jsx
文件。
import { getImageUrl } from "./utils";
function Avatar({ person, size }) {
const defaultStyle = {
borderRadius: "50%",
padding: "5px",
};
return (
<>
<img
style={defaultStyle}
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
</>
);
}
export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: "Katsuko Saruhashi",
imageId: "YfeOqp2",
}}
/>
<Avatar
size={80}
person={{
name: "Aklilu Lemma",
imageId: "OKS67lh",
}}
/>
<Avatar
size={50}
person={{
name: "Lin Lanying",
imageId: "1bX5QH6",
}}
/>
</div>
);
}
创建 src/utils.js
文件,里面编写工具类函数。
export function getImageUrl(person) {
return "https://i.imgur.com/" + person.imageId + ".jpg";
}
将内容嵌套在 JSX 标签中时,父组件将在名为 children 的 prop 中接收到该内容。例如,下面的 Card 组件将接收一个被设为 <Avatar />
的 children prop 并将其包裹在 div 中渲染。
创建 src/Avatar.jsx
文件,放置头像。
import { getImageUrl } from "./utils";
export default function Avatar({ person, size }) {
const defaultStyle = {
borderRadius: "50%",
padding: "5px",
};
return (
<img
style={defaultStyle}
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
修改 src/Profile.jsx
文件,添加 Card 方法,注意参数传递了 children
,且在返回值中的 {children}
。
import Avatar from "./Avatar";
function Card({ children }) {
const cardStyle = {
padding: "20px",
borderRadius: "10px",
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
backgroundColor: "#fff",
};
return <div style={cardStyle}>{children}</div>;
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: "Katsuko Saruhashi",
imageId: "YfeOqp2",
}}
/>
<Avatar
size={80}
person={{
name: "Aklilu Lemma",
imageId: "OKS67lh",
}}
/>
<Avatar
size={50}
person={{
name: "Lin Lanying",
imageId: "1bX5QH6",
}}
/>
</Card>
);
}
条件渲染
在 React 中,你可以通过使用 JavaScript 的 if 语句、&& 和 ? : 运算符来选择性地渲染 JSX。
创建 PackingList.jsx
文件,通过判断控制哪些事情已经完成。
function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = <del>{name + " ✅"}</del>;
}
return <li className="item">{itemContent}</li>;
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride 的行李清单</h1>
<ul>
<Item isPacked={true} name="宇航服" />
<Item isPacked={true} name="带金箔的头盔" />
<Item isPacked={false} name="Tam 的照片" />
</ul>
</section>
);
}
渲染列表
从数组中过滤并渲染数据。
创建 src/data.js
文件,存放数组信息。
export const people = [
{
id: 0,
name: "凯瑟琳·约翰逊",
profession: "数学家",
accomplishment: "太空飞行相关数值的核算",
imageId: "MK3eW3A",
},
{
id: 1,
name: "马里奥·莫利纳",
profession: "化学家",
accomplishment: "北极臭氧空洞的发现",
imageId: "mynHUSa",
},
{
id: 2,
name: "穆罕默德·阿卜杜勒·萨拉姆",
profession: "物理学家",
accomplishment: "关于基本粒子间弱相互作用和电磁相互作用的统一理论",
imageId: "bE7W1ji",
},
{
id: 3,
name: "珀西·莱温·朱利亚",
profession: "化学家",
accomplishment: "开创性的可的松药物、类固醇和避孕药的研究",
imageId: "IOjWm71",
},
{
id: 4,
name: "苏布拉马尼扬·钱德拉塞卡",
profession: "天体物理学家",
accomplishment: "白矮星质量计算",
imageId: "lrWQx8l",
},
];
创建 RenderList.jsx
文件,使用 filter
和 map
,过滤并展示出化学家的相关信息。注意给数组里面的每一项都指定一个 key
,进行唯一标识。
import { getImageUrl } from "./utils";
import { people } from "./data.js";
export default function List() {
const avatarStyle = {
borderRadius: "50%",
height: "100px",
width: "100px",
};
const chemists = people.filter((person) => person.profession === "化学家");
const listItems = chemists.map((person) => (
<li key={person.id}>
<img style={avatarStyle} src={getImageUrl(person)} alt={person.name} />
<p>
<b>{person.name}:</b>
{" " + person.profession + " "}因 {person.accomplishment} 而闻名全世界!
</p>
</li>
));
return <ul>{listItems}</ul>;
}
保持组件纯粹
- 一个组件必须是纯粹的,就意味着:
- 只负责自己的任务。 它不会更改在该函数调用前就已存在的对象或变量。
- 输入相同,则输出相同。给定相同的输入,组件应该总是返回相同的 JSX。
- 渲染随时可能发生,因此组件不应依赖于彼此的渲染顺序。
- 你不应该改变任何用于组件渲染的输入。这包括 props、state 和 context。通过 “设置” state 来更新界面,而不要改变预先存在的对象。
- 努力在你返回的 JSX 中表达你的组件逻辑。当你需要“改变事物”时,你通常希望在事件处理程序中进行。作为最后的手段,你可以使用 useEffect。
将 UI 视为树
组件的一个主要特性是能够由其他组件组合而成。在 嵌套组件 中有父组件和子组件的概念,其中每个父组件本身可能是另一个组件的子组件。
下面我们使用 React 应用程序渲染一些鼓舞人心的引语。
修改 src/App.jsx
文件。
import "./App.css";
import FancyText from "./FancyText";
import InspirationGenerator from "./InspirationGenerator";
import Copyright from "./Copyright";
function App() {
return (
<>
<FancyText title text="励志语录" />
<InspirationGenerator>
<Copyright year={2025} />
</InspirationGenerator>
</>
);
}
export default App;
创建 src/FancyText.jsx
文件,显示展示 “励志语录”。
export default function FancyText({ title, text }) {
return title ? (
<h1 className="fancy title">{text}</h1>
) : (
<h3 className="fancy cursive">{text}</h3>
);
}
创建 src/InspirationGeneration.jsx
文件,显示主体部分。
import * as React from "react";
import quotes from "./quotes";
import FancyText from "./FancyText";
export default function InspirationGenerator({ children }) {
const [index, setIndex] = React.useState(0);
const quote = quotes[index];
const next = () => setIndex((index + 1) % quotes.length);
return (
<>
<p>你的励志名言是:</p>
<FancyText text={quote} />
<button onClick={next}>再次激励我</button>
{children}
</>
);
}
创建 Copyright.jsx
文件,显示版权。
export default function Copyright({ year }) {
return <p className="small">©️ {year}</p>;
}
创建 quotes.js
文件,存放语录。
export default [
"生活就像骑自行车,要保持平衡就得不断前进",
"每个人都是自己命运的建筑师",
"不要让别人的意见左右你的决定",
"人生的意义在于追求和实现自己的梦想",
];
总结
本篇文章,我们学习了 React 官方文档中讲述的 描述UI 的内容,总体上不算难,一些写法需要多加练习和记忆,下面文章我们学习 添加交互 部分。
好啦,本篇文章到这里就要和大家说再见啦,祝你这篇文章阅读愉快,你下篇文章的阅读愉快留着我下篇文章再祝!
参考资料:
- React 官方文档:React 官方文档
- DeepSeek:DeepSeek
- VS Code · Copilot