REACT
1 定义一个组件
export default function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
)
}
一个组件必须是大写字母开头,不然不会生效。
第一步:导出组件
export default
是js
语法,意思是默认导出,一个组件里面只能有一个默认导出。这样允许你能够在另外一个组件引用它。
第二步:定义函数
function Profile(){}
第三步:添加标签
这个组件允许返回img
标签,看似是html
,但实际上是js
,这种语法被称为jsx
返回语句可以写在一行上
return<img src="https://i.imgur.com/MK3eW3Am.jpg" alt="Katherine Johnson"/>
也可以像上面的例子一样用()
注意:组件不能嵌套定义而且应该在顶层声明组件
总结:
- React 是常规的 JavaScript 函数,除了:
- 它们的名字总是以大写字母开头。
- 它们返回 JSX 标签。
2 组件的导入与导出
- 创建一个新的JS文件来存放该组件
- 导出该组件的函数组件(可以使用默认导出或具名导出)
- 在需要使用该组件的文件导入
2.1 默认导入和导出
export - JavaScript | MDN (mozilla.org)
默认导入和具名导出是js
原本的语法,一个页面可以有多个具名导出。
例:
// 导出单个特性
export let name1 =1
export function FunctionName(){}
export class ClassName{}
但是只能有一个默认导出,不然会报错。
2.2 默认导入和具名导入
也就是说,当这个组件使用默认导出的时候,如果你想在其他地方引用这个组件,导入名可以不跟导出名一致。而具名导入则相反
要求导入和导出的名字必须相同。
3 JSX
react
使用jsx
语法来返回html
标签,这是因为由于渲染逻辑和标签是紧密相关的,所以 React 将它们存放在一个组件中。jsx
是js
的扩展,JSX 看起来和 HTML 很像,但它的语法更加严格并且可以动态展示信息。
jsx
语法要求一个只能返回一个根组件,所以当你想要返回多个标签的时候,需要有一个父标签来包裹这些标签,可以是<div>
和</div>
或者是< >
和< / >
(这个空标签被称作 Fragment。React Fragment 允许你将子元素分组,而不会在 HTML 结构中添加额外节点。)。
那么为什么需要用一个父标签来进行包裹呢,因为在JSX
底层,代码都被转换为JS
对象,你不能在一个函数中返回多个对象,但是可以把多个对象放在一个数组里面,然后返回一个数组,这就是为什么JSX需要父元素或者 *Fragment*进行包裹。
3.1 使用JSX编写JS逻辑和引用动态属性
1.使用引号传递字符串
可以使用''
或""
来把一个字符串属性传递给JSX
,如果需要动态的指定则需要用{}
2.可以在哪使用大括号
在 JSX 中,只能在以下两种场景中使用大括号:
- 用作 JSX 标签内的文本:
<h1>{name}'s To Do List</h1>
- 用作紧跟在
=
符号后的 属性:src={avatar}
会读取avatar
变量,但是src="{avatar}"
只会传一个字符串{avatar}
。
3.使用双大括号代表包裹了一个对象
例:
<ul style={
{
backgroundColor: 'black',
color: 'pink'
}
}>
请注意内联 style
属性 使用驼峰命名法编写。对象中的属性可以通过.
来获取。
JSX 元素不是“实例”,因为它们没有内部状态也不是真实的 DOM 节点。
4 将 Props 传递给组件
此时是未进行传参的原代码
function Avatar() {
return (
<img
className="avatar"
src="https://i.imgur.com/1bX5QH6.jpg"
alt="Lin Lanying"
width={100}
height={100}
/>
);
}
export default function Profile() {
return (
<Avatar />
);
}
步骤 1: 将 props 传递给子组件
首先,将一些 props 传递给 Avatar
。例如,让我们传递两个 props:person
(一个对象)和 size
(一个数字):
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
步骤 2: 在子组件中读取 props
你可以通过在 function Avatar
之后直接列出它们的名字 person, size
来读取这些 props。这些 props 在 ({
和 })
之间,并由逗号分隔。
function Avatar({ person, size }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
);
}
请注意虽然Props
不是静态的,但是它也是不可变二点,这意味着子组件不能更改父组件传过来的Props
,只能请求父组件传递不同的props
,但是如果需要响应用户的选择时,可以设置state
来进行改变。
5 条件渲染
在 React 中,你可以通过使用 JavaScript
的if
语句、&&
和 ?
:
运算符来选择性地渲染 JSX
。
如果 isPacked
属性是 true
,这段代码会返回一个不一样的 JSX。通过这样的改动,一些物品的名字后面会出现一个勾选符号:
例:
function Item({ name, isPacked }) {
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
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>
);
}
还可以使用三目运算符
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
JavaScript && 表达式 :意思是
左侧(我们的条件)为 true
时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false
,则整个表达式会变成 false
- 在 JSX 中,
{cond ? <A /> : <B />}
表示 “当cond
为真值时, 渲染<A />
,否则<B />
”。 - 在 JSX 中,
{cond && <A />}
表示 “当cond
为真值时, 渲染<A />
,否则不进行渲染”。
6 渲染列表
JavaScript 的 filter()
方法会让数组的子项经过 “过滤器”(一个返回值为 true
或 false
的函数)的筛选,最终返回一个只包含满足条件的项的新数组。
const chemists = people.filter(person =>
person.profession === '化学家'
);
这段代码是在people
里进行筛选,选出职业为化学家的人,并返回一个只包含化学家的新数组。
注意
箭头函数会隐式地返回位于 =>
之后的表达式,所以你可以省略 return
语句。
const listItems = chemists.map(person =>
<li>...</li> // 隐式地返回!
);
不过,如果你的 =>
后面跟了一对花括号 {
,那你必须使用 return
来指定返回值!
const listItems = chemists.map(person => { // 花括号
return <li>...</li>;
});
箭头函数 => {
后面的部分被称为 “块函数体”,块函数体支持多行代码的写法,但要用 return
语句才能指定返回值。假如你忘了写 return
,那这个函数什么都不会返回!
直接放在 map()
方法里的 JSX 元素一般都需要指定 key
值!
这些 key
会告诉 React
,每个组件对应着数组里的哪一项,所以 React
可以把它们匹配起来。这在数组项进行移动(例如排序)、插入或删除等操作时非常重要。一个合适的 key
可以帮助 React
推断发生了什么,从而得以正确地更新 DOM
树。
如果你想在以上的示例中,在<li></li>
标签里渲染多个节点,可以使用<Fragment>
写法
const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
这里的 Fragment
标签本身并不会出现在 DOM
上,这串代码最终会转换成 <h1>
、<p>
、<h1>
、<p>
…… 的列表。
key 需要满足的条件
- key 值在兄弟节点之间必须是唯一的。 不过不要求全局唯一,在不同的数组中可以使用相同的 key。
- key 值不能改变,否则就失去了使用 key 的意义!所以千万不要在渲染时动态地生成 key。
可以做一些练习:https://react.docschina.org/learn/rendering-lists#challenges
7 保证组件的纯粹
一个纯粹的组件应该包含以下要求:
- 只负责自己的任务,不会在函数调用前就更改已经存在的对象或者变量
- 给定相同的输出,永远返回相同的JSX
- 不依赖其他组件的渲染顺序
值得一提的是react
和vue
一样要求传入的props
不能直接被改变,而是要求直接重新传入一个全新的props
在 React 中,你可以在渲染时读取三种输入:props,state 和 context。你应该始终将这些输入视为只读。
而React
中的纯函数代表仅执行计算操作,不做其他操作,拒绝任何副作用,能使得组件变得单纯。每个组件要求步依赖其他组件渲染,也不和其他组件进行协调,保证了组件的独立性。
副作用通常通过React的事件处理程序来处理,因为事件处理程序无需是纯函数,即使是在组件内部定义但不会在渲染期间运行。
切记不要改变预先存在的对象或者变量
如果组件预先改变了预先存在的变量,这种情况叫做mutation
。不过如果你是在渲染时,改变变量,这是允许存在的。
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
因为每次渲染时,你都是在 TeaGathering
函数内部创建的它们。TeaGathering
之外的代码并不会知道发生了什么。这就被称为 “局部 mutation”
请注意:当你想要更改数组的任意项时,必须先对其进行拷贝。push
、pop
、reverse
和 sort
会改变原始数组,但 slice
、filter
和 map
则会创建一个新数组。
7.1 为什么纯函数如此重要
- 纯函数针对相同的输出,永远返回相同的结果,因此一个组件就能满足多个用户的需求
- 可以为输入未更改的组件来跳过渲染,因为纯函数总还是返回相同的结果,可以安全的缓存已经计算的结果
可以做一些练习:https://react.docschina.org/learn/rendering-lists#challenges