背景:React现在已经更新到19了,文档地址也做了全面的更新,上一次系统性的学习还是在16-17的大版本更新。所以,现在就开始重新学习吧~
学习内容:
- React官网教程:https://zh-hans.react.dev/learn/describing-the-ui
- 其他辅助资料(看到再补充)
补充说明:这次学习更多的是以学习笔记的形式记录,看到哪记到哪
1. 基础知识
React 应用是由被称为 组件 的独立 UI 片段构建而成。React 组件本质上是可以任意添加标签的 JavaScript 函数
React 允许你将标签、CSS 和 JavaScript 组合成自定义“组件”,即 应用程序中可复用的 UI 元素
React 组件是一段可以使用标签进行扩展 的 JavaScript 函数,组件的名称必须以大写字母开头(React的语法规定,这样它才能分清是React组件还是正常的html标签)
// export default 导出声明
// function Profile 定义函数,function名必须首字母大写
export default function Profile() {
// return 如果换行,就必须用()将内容包裹
// 没有括号包裹的话,任何在 return 下一行的代码都将被忽略!
return (
<img
src="https://i.imgur.com/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
)
// 或者 标签只有一行的时候括号可以省略
return <img src="https://i.imgur.com/MK3eW3Am.jpg" alt="Katherine Johnson" />
)
}
// 你可以只定义组件一次,然后按需多处和多次使用
export default function Gallery() {
return (
<section>
<h1>了不起的科学家</h1>
<Profile />
<Profile />
<Profile />
</section>
);
}
// 组件不建议嵌套组件定义,不然会很慢并且可能会有bug产生
export default function Gallery() {
// 🔴 永远不要在组件中定义组件
function Profile() {
// ...
}
// ...
}
// 使用时可以import './Gallery.js' 或者 './Gallery',在 React 里都能正常使用,只是前者更符合 原生 ES 模块
import Gallery from './Gallery';
import Gallery from './Gallery.js';
导出方式
默认导出 vs 具名导出
一个文件里有且仅有一个 默认 导出,但是可以有任意多个 具名 导出
当使用默认导入时,可以在 import 语句后面进行任意命名。比如 import Banana from ‘./Button.js’。相反,对于具名导入,导入和导出的名字必须一致。
同一文件中,有且仅有一个默认导出,但可以有多个具名导出
语法 | 导出语句 | 导入语句 |
---|---|---|
默认 | export default function Button() {} | import Button from ‘./Button.js’; |
具名 | export function Button() {} | import { Button } from ‘./Button.js’; |
JSX
- 只能返回一个根元素
在一个组件中包含多个元素,需要用一个父标签把它们包裹起来,如果不想添加新的dom元素,可以使用<>...</>(Fragment)
,React Fragment 允许将子元素分组,而不会在 HTML 结构中添加额外节点
原因:JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来 - 标签必须闭合
这是强制规定,要么自闭合(<img />
),要么添加闭合标签(<li>...</li>
) - 使用驼峰式命名法给大部分属性命名
JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。组件经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制,所以需要避开这些限制- 变量名称不能包含 - 符号,所以属性大部分用驼峰
- 变量不能用保留字如class,所以在jsx中用className代替
- 由于历史原因,aria-* 和 data-* 属性是以带 - 符号的 HTML 格式书写的
需要将一个字符串属性传递给 JSX 时,把它放到单引号或双引号
// "" 引号中的内容按照字符串的形式处理,单引号双引号都可以,但使用双引号会多点
// {} 大括号中的内容会被动态引用,直接在标签中使用 JavaScript,可以在其他地方声明,在使用的时候直接读取js对应的值
// 大括号内的任何 JavaScript 表达式都能正常运行
export default function Avatar() {
const alt = "Gregorio Y. Zara"
//
return (
<img
className="avatar"
src="https://i.imgur.com/7vQD0fPs.jpg"
alt={alt}
/>
);
}
大括号使用场景:
- 用作 JSX 标签内的文本:
<h1>{name}'s To Do List</h1>
是有效的,但是<{tag}>Gregorio Y. Zara's To Do List</{tag}>
无效。 - 用作紧跟在 = 符号后的 属性:
src={avatar}
会读取 avatar 变量,但是src="{avatar}"
只会传一个字符串 {avatar}
在JSX中还可以传递对象,对象也是用大括号表示,所以要引用对象的时候就需要使用两个括号
JSX 是一种模板语言的最小实现,因为它允许你通过 JavaScript 来组织数据和逻辑
Props
React 组件使用 props 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它,包括对象、数组和函数等
import { getImageUrl } from './utils.js';
// props是组件的唯一参数
// function里使用大括号获取props是解构
// 也可以写成这样
// function Avatar(props) {
// const person = props.person
// const size = props.size
// }
// 如果你想在没有指定值的情况下给 prop 一个默认值,可以通过在参数后面写 = 和默认值来进行解构
// 默认值仅在缺少 size prop 或 size={undefined} 时生效,等于null都不行
function Avatar({ person, size=100 }) {
// person 和 size 是可访问的
return (
<img
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'
}}
/>
</div>
);
}
// 还可以使用 JSX 展开语法传递 props
// 像这个场景,props里面所有的内容都是需要传递到Avatar组件中时,就可以直接用展开语法传递
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
// 可以写成这样
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
// 假设Profile 中isSepia不需要传递,其他都需要,还可以写成这样
function Profile({isSepia, ...rest}) {
return (
<div className="card">
<Avatar {...rest} />
</div>
);
}
当你将内容嵌套在 JSX 标签中时,父组件将在名为 children 的 prop 中接收到该内容
通俗的来说就是某个组件标签内容,在接收的时候会自动处理成children的props
<Aaa><div>里面是一系列的内容</div> </Aaa>
,在声明Aaa这个组件时,有一个隐藏props,children,表示的就是div及其包裹的内容
可以将带有 children prop 的组件看作有一个“洞”,可以由其父组件使用任意 JSX 来“填充”
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
props 是 不可变的。当一个组件需要改变它的 props(例如,响应用户交互或新数据)时,它不得不“请求”它的父组件传递 不同的 props —— 一个新对象!它的旧 props 将被丢弃,最终 JavaScript 引擎将回收它们占用的内存
Props 是只读的时间快照:每次渲染都会收到新版本的 props
你不能改变 props。当你需要交互性时,你可以设置 state。
渲染
在 React 中,你可以通过使用 JavaScript 的 if 语句、&& 和 ? : 运算符来选择性地渲染 JSX