组件是用户界面UI的构成要素。创建一个组件可以分为三步:
导出组件
export default
定义函数
function name(props) {}
定义组件的时候,组件的名称必须以大写字母开头,否则将无法运行。
HTML
标签是小写的
添加标签
JSX
(使用JavaScript
)
TSX
(使用TypeScript
)
注意:如果标签和return
不在同一行,则必须把它包裹在一堆括号中,
没有括号包裹的话,任何在return
下一行的代码都将被忽略!
在父组件中引入即可,但是不要在组件中定义组件。应该在顶层定义组件。当子组件需要使用父组件的数据时,需要 通过 props 的形式进行传递,而不是嵌套定义。
相当于把所有模块化组件做一个集成的文件。
对组件进行拆分可以分为三步:
语法 | 导出语句 | 导入语句 |
---|---|---|
默认 | export default function Hello() {} |
import Hello from './Hello.js'; |
具名 | export function Hello() {} |
import { Hello } from './Hello.js'; |
注意:在引入一些文件时虽然没有添加.js
文件名后缀,比如./Hello.js
还是./Hello
在React
里都能正常使用,只是前者更符合原生ES模块
一个文件里有且仅有一个默认导出,但是可以有任意多个具名导出。组件的导出方式决定了其导入方式。通常,文件中仅包含一个组件时,会选择默认导出,而当文件中包含多个组件或某个值需要导出时,则会选择具名导出。
同一文件中,有且仅有一个默认导出,但可以有多个具名导出!
JSX是JavaScript语法扩展,可以在JavaScript文件中写类似HTML的标签。(虚拟DOM
)
**在 React 中,渲染逻辑和标签共同存在于同一个地方——组件。**将一个按钮的渲染逻辑和标签放在一起可以确保它们在每次编辑时都能保持互相同步。每个 React
组件都是一个 JavaScript
函数,它会返回一些标签,React 会将这些标签渲染到浏览器上。
JSX
和 React
是相互独立的东西。JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。
JSX规则:
只能返回一个根元素
如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来。
如果不想在标签中增加一个额外的 这个空标签被称作 Fragment. React Fragment 允许将子元素分组,而不会在 HTML 结构中添加额外节点。 为什么多个 JSX 标签需要被一个父元素包裹? 答:JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。 标签必须闭合 使用驼峰式命名法给大部分属性命名 将HTML转换为JSX:https://transform.tools/html-to-jsx 想把一个字符串属性传递给 JSX 时,把它放到单引号或双引号中 若想要动态指定可以使用 大括号内的任何JavaScript表达式都可以正常运行。 在 JSX 中,只能在以下两种场景中使用大括号: 对象也用大括号表示,例如 父组件可以分两步将props传递给子组件 首先在父组件中将 在子组件中读取 如果想在没有指定值的情况下给 但是如果传递了 先看两段代码感受一下 是不是觉得第二段用了展开语法的代码更简洁!但是这通常表示你应该拆分组件了,并需要将子组件作为JSX传递。 当将内容嵌套在 JSX 标签中时,父组件将在名为 可以将带有 **一个组件可能会随着时间的推移收到不同的 可以用 例如: 实际上,在组件里返回 可以使用三目运算符 或者用 与表达式的判定若为 结合 操作数组中的数据,将一个数据集渲染成多个相似的组件。 利用 注意:这里还未给每个数据加上唯一标识符 先利用 注意:这里还未给每个数据加上唯一标识符 箭头函数会隐式地返回位于 必须给数组中的每一项都指定一个 直接放在 一个合适的 如果想让每个列表项都输出多个 DOM 节点而非一个的话,只能要么把生成的节点用一个 这里的 Fragment 标签本身并不会出现在 DOM 上,这串代码最终会转换成 key值的设定: key需要满足的条件: React之所以需要 注意,组件不会把 挑战题: 请根据给你的数组生成菜谱列表!其中每个菜谱,都用 一个React组件,不管什么时候给了它一个相同的输入,那么它的输出就是相同的,就像数学公式一样。比如 React 无法保证组件函数以任何特定的顺序执行,因此无法通过设置变量在它们之间进行通信。所有的交流都必须通过 props 进行。 我们将组件改变了预先存在的变量的值的现象称为 但是事件处理程序无需是纯函数,包括更新屏幕、启动动画、更改数据等。 React为何侧重于纯函数?<>
和 >
元素来代替:
aria-*
和 data-*
属性是以带 -
符号的 HTML 格式书写的。1.3.3 高级提示:使用JSX转化器
1.4 在JSX中通过大括号使用JavaScript
1.4.1 使用引号传递字符串
{}
是有效的,但是 {name}'s To Do List
<{tag}>Gregorio Y. Zara's To Do List{tag}>
无效。=
符号后的 属性:src={avatar}
会读取 avatar
变量,但是 src="{avatar}"
只会传一个字符串 {avatar}
。1.4.2 使用"双大括号":JSX中的CSS和对象
{ name: "无敌是多么寂寞", inventions: 5 }
。因此,为了能在 JSX 中传递,你必须用另一对额外的大括号包裹对象:person={{ name: "无敌是多么寂寞", inventions: 5 }}
。import { getImageUrl } from './utils.js'
const person = {
name: 'Gregorio Y. Zara',
imageId: '7vQD0fP',
imageSize: 's',
theme: {
backgroundColor: 'black',
color: 'pink'
}
};
export default function TodoList() {
return (
<div style={person.theme}>
<h1>{person.name}'s Todos</h1>
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
/>
<ul>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
</div>
);
}
// 将获取图片的语句封装成一个函数
export function getImageUrl(person) {
return (
'https://i.imgur.com/' +
person.imageId +
person.imageSize +
'.jpg'
);
}
1.5 将Props传递给组件
React
组件使用 props 来互相通信。每个父组件都可以提供 props
给它的子组件,从而将一些信息传递给它。Props
可能会让你想起 HTML
属性,但你可以通过它们传递任何 JavaScript
值,包括对象、数组和函数。1.5.1 将Props传递给组件
props
传递给引用的子组件,使得子组件可以读取这些props
props
,这些 props 在 ({
和 })
之间,并由逗号分隔。比如function Avatar({ person, size }) {
// 在这里 person 和 size 是可被子组件访问的
}
props
正是 组件的唯一参数! React 组件函数接受一个参数,一个 props
对象。比如:function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
1.5.2 给props指定一个默认值
prop
一个默认值,可以通过在参数后面写 = 默认值
来进行解构,比如:function Avatar({ person, size = 100 }) {
// ...
}
size={null}
或 size={0}
,默认值将 不 被使用。1.5.3 使用JSX展开语法传递props
// 第一段
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>
);
}
1.5.4 将JSX作为子组件传递
children
的 prop 中接收到该内容。function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<h1>哎哟不错哦</h1>
</Card>
);
}
children
prop 的组件看作有一个“洞”,可以由其父组件使用任意 JSX 来“填充”。1.5.5 props如何随时间变化
props
。**但是props
的改变是父组件通过传递不同的props
——新对象来改变的。旧props
会被丢弃然后被JS引擎回收它们占用的内存.当需要交互性时,可以设置 state
。1.6 条件渲染
1.6.1 条件返回JSX
if else、&&、? :
语句来写分支逻辑1.6.2 选择性地返回null
function Item({ name, isPacked }) {
if (isPacked) {
return null;
}
return <li className="item">{name}</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>
);
}
null
并不常见。通常情况下,可以在父组件里选择是否要渲染该组件。选择性地包含JSX
? :
。如果代码有小段重复可以考虑用这个进行简单的条件判断。比如:// 修改前
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
// 修改后
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
&&
运算符 return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
false
,React会将其视为控制,不会对其进行任何渲染。但切勿把数字放在&&
左侧,因为如果左侧是0
,React会渲染0
。选择性地将JSX赋值给变量
if
语句和let
赋值变量可以更加灵活。因为可以使用 let
进行重复赋值,所以一开始可以将想展示的(这里指的是物品的名字)作为默认值赋予给该变量。function Item({ name, isPacked }) {
let itemContent = name;
if (isPacked) {
itemContent = name + " ✔";
}
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>
);
}
1.7 渲染列表
1.7.1 从数组中渲染数据
map()
函数const people = [
'凯瑟琳·约翰逊: 数学家',
'马里奥·莫利纳: 化学家',
'穆罕默德·阿卜杜勒·萨拉姆: 物理学家',
'珀西·莱温·朱利亚: 化学家',
'苏布拉马尼扬·钱德拉塞卡: 天体物理学家',
];
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
key
1.7.2 对数组项进行过滤
filter()
函数进行筛选,再利用map()
函数进行渲染import { people } from './data.js';
import { getImageUrl } from './utils.js';
export default function List() {
const chemists = people.filter(person =>
person.profession === '化学家'
);
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
因{person.accomplishment}而闻名世界
</p>
</li>
);
return <ul>{listItems}</ul>;
}
key
.=>
之后的表达式,所以可以省略 return
语句。不过,如果 =>
后面跟了一对花括号 {
,那必须使用 return
来指定返回值! 箭头函数 => {
后面的部分被称为 “块函数体”,块函数体支持多行代码的写法,但要用 return
语句才能指定返回值。
1.7.3 用key保持列表项的顺序
key
——它可以是字符串或数字的形式,只要能唯一标识出各个数组项就行。
map()
方法里的 JSX 元素一般都需要指定 key
值!key
可以帮助 React 推断发生了什么,从而得以正确地更新 DOM 树。
写法:
import { Fragment } from 'react';
// ...
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
、
、
、
…… 的列表。
uuid
的库来生成 key。
key
值,是因为即使元素的位置在渲染的过程中发生了改变,它提供的 key
值也能让 React 在整个生命周期中一直认得它。
key
当作 props 的一部分。Key 的存在只对 React 本身起到提示作用。如果你的组件需要一个 ID,那么请把它作为一个单独的 prop 传给组件:
。 来显示它的名称,并在
里列出它所需的原料。data.js
export const recipes = [
{
id: 'greek-salad',
name: '希腊沙拉',
ingredients: ['西红柿', '黄瓜', '洋葱', '油橄榄', '羊奶酪'],
},
{
id: 'hawaiian-pizza',
name: '夏威夷披萨',
ingredients: ['披萨饼皮', '披萨酱', '马苏里拉奶酪', '火腿', '菠萝'],
},
{
id: 'hummus',
name: '鹰嘴豆泥',
ingredients: ['鹰嘴豆', '橄榄油', '蒜瓣', '柠檬', '芝麻酱'],
},
];
App.js
import { recipes } from './data.js';
function RecipeShow({ recipesList }) {
return (
<div>
{recipesList.map(recipe =>
<div key={recipe.id}>
<h2>{recipe.name}</h2>
<ul>
{recipe.ingredients.map(ingredent =>
<li key={ingredent}>
{ingredent}
</li>
)}
</ul>
</div>
)}
</div>
)
}
function Card({children}) {
return (
<div>{ children }</div>
)
}
export default function RecipeList() {
return (
<Card>
<div>
<h1>菜谱</h1>
<RecipeShow recipesList={recipes} />
</div>
</Card>
);
}
1.8 保持组件为纯函数
y=2x
,无论何时传x=3
,其输出总是y=6
。1.8.1 局部
mutation
突变(mutation)
。局部突变的意思就是在渲染时更改刚刚创建的变量和对象,属于内部突变,不会影响函数外部的变量或函数。