描述UI
渲染列表
- 我们将使用
map
,filter
生成组件,并绑定React的key
- 从数组中渲染数据
- 将数组变得更加格式化
箭头函数隐式返回,可以省略return. 但是如果有一对花括号,必须显式使用return返回值
- 使用key来保持列表顺序(字符串或者数字,唯一标识就可以)
一般map都需要指定key值,一般来说key需要提前准备好
- 如果列表希望返回多个Dom节点而不是一个,可以用
Fragment
- 如何设置key(数据库的主键/本地的uuid)
- key值需要满足的条件(兄弟节点之间必须是唯一的,不要求全局唯一/key值不能改变)
补充:
1.key值不传,数组索引将作为默认,这可能导致一些令人疑惑的Bug
2.不要动态的key(key={Math.random})会导致每次都是渲染一个新的Dom,导致变慢
3.需要Id需要额外设定,Key不会作为组件props属性一部分
const people = [
'凯瑟琳·约翰逊: 数学家',
'马里奥·莫利纳: 化学家',
'穆罕默德·阿卜杜勒·萨拉姆: 物理学家',
'珀西·莱温·朱利亚: 化学家',
'苏布拉马尼扬·钱德拉塞卡: 天体物理学家',
];
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
// ----------- section 3
import { getImageUrl } from './utils.js';
const people = [{
id: 0,
name: '凯瑟琳·约翰逊',
profession: '数学家',
}, {
id: 1,
name: '马里奥·莫利纳',
profession: '化学家',
}];
export default function List() {
const chemists = people.filter(person =>//这里进行过滤 保留返回函数值为true的数组项
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>;
}
// ----------- section 5
const listItems = people.map(person =>
<Fragment key={person.id}>//Fragment需要单独引入
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);
摘要
在这篇文章中,你学习了:
- 如何从组件中抽离出数据,并把它们放入像数组、对象这样的数据结构中。
- 如何使用map() 方法来生成一组相似的组件。
- 如何使用filter() 方法来筛选数组。
- 为何以及如何给集合中的每个组件设置一个 key 值:它使 React 能追踪这些组件,即便后者的位置或数据发生了变化。
保持组件纯粹
- 纯函数两个特点(只负责自己任务,不会调用已经存在的对象或变量/输入相同,结果相同)
- 副作用预期的后果: 多次调用这个组件会产生不同的 JSX
一般来说,你不应该期望你的组件以任何特定的顺序被渲染。调用 y = 5x 和 y = 2x 的先后顺序并不重要:这两个公式相互独立。同样地,每个组件也应该“独立思考”,而不是在渲染过程中试图与其他组件协调,或者依赖于其他组件。渲染过程就像是一场学校考试:每个组件都应该自己计算 JSX
- React的严格模式([HTML_REMOVED]包裹根组件,默认生产环境不生效,开发环境会运行组件两次尝试找出非纯函数)
- 三种输入(props,context,state)都应该是只读的
let guest = 0;
function Cup() {
// Bad:正在更改预先存在的变量!
guest = guest + 1;
return <h2>Tea cup for guest #{guest}</h2>;// 多次调用这个组件会产生不同的 JSX 可以通过将guest作为参数修复
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
);
}
突变Mutation
- 组件改变了预先存在的变量的值,叫做突变(mutation)
- 纯函数不会改变函数作用域外的变量或者函数调用前创建的对象-这会让函数变得不纯粹(这里的对象,因为执行的时候,默认值可以取到调用处的变量)
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaGathering() {
let cups = [];//这里是在 TeaGathering 里边创建的 所以这里不会更改预先存在的对象,外部不知道会发生什么叫做局部mutation
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
哪些地方会引发副作用
- 函数式编程在很大程度上依赖于纯函数,但 某些事物 在特定情况下不得不发生改变。这是编程的要义!这些变动包括更新屏幕、启动动画、更改数据等,它们被称为 副作用。它们是 “额外” 发生的事情,与渲染过程无关。
- 在 React 中,副作用通常属于 事件处理程序。事件处理程序是 React 在你执行某些操作(如单击按钮)时运行的函数。
- 即使事件处理程序是在你的组件 内部 定义的,它们也不会在渲染期间运行! 因此事件处理程序无需是纯函数。
- 如果你用尽一切办法,仍无法为副作用找到合适的事件处理程序,你还可以调用组件中的 useEffect 方法将其附加到返回的 JSX 中。这会告诉 React 在渲染结束后执行它。然而,这种方法应该是你最后的手段。
摘要
- 一个组件必须是纯粹的,就意味着:
-
- 只负责自己的任务。 它不会更改在该函数调用前就已存在的对象或变量。
-
- 输入相同,则输出相同。 给定相同的输入,组件应该总是返回相同的 JSX。
- 渲染随时可能发生,因此组件不应依赖于彼此的渲染顺序。
- 你不应该改变任何用于组件渲染的输入。这包括 props、state 和 context。通过 “设置” state 来更新界面,而不要改变预先存在的对象。
- 努力在你返回的 JSX 中表达你的组件逻辑。当你需要“改变事物”时,你通常希望在事件处理程序中进行。作为最后的手段,你可以使用 useEffect。
- 编写纯函数需要一些练习,但它充分释放了 React 范式的能力。
尝试一些挑战
// 修复坏掉的时钟
export default function Clock({ time }) {
let hours = time.getHours();
if (hours >= 0 && hours <= 6) {//第二个解决方法 就是计算好className 在return 中 {{className}}
document.getElementById('time').className = 'night';//这个DOM 副作用 完全没有必要 直接返回JSX就可以了
} else {
document.getElementById('time').className = 'day';
}
return (
<h1 id="time">
{time.toLocaleTimeString()}
</h1>
);
}
//my answer
export default function Clock({ time }) {
let hours = time.getHours();
return (
<h1 id="time" className={hours>=0&&hours<=6?'night':'day'}>//这个就会推迟到渲染的时候 进行
{time.toLocaleTimeString()}
</h1>
);
}
// ----------- section 2 修复损坏的材料
import Panel from './Panel.js';
import { getImageUrl } from './utils.js';
let currentPerson;//这个变量让三个函数 都不在纯粹 本组件点击一次之后 两个Profile显示都相同了
export default function Profile({ person }) {
currentPerson = person;
return (
<Panel>
<Header />
<Avatar person={person}/>//提示词1
</Panel>
)
}
function Header() {
return <h1>{currentPerson.name}</h1>;
}
function Avatar() {
return (
<img
className="avatar"
src={getImageUrl(currentPerson)}
alt={currentPerson.name}
width={50}
height={50}
/>
);
}
// ----------- section 3
export default function StoryTray({ stories }) {//坏掉的时钟 发现createStroy出现了不止一次
stories.push({//默认生产环境 发生了Mutation 严格模式调用了两次 push 暗示我们渲染过程中出现了mutation 难以预测
id: 'create',
label: 'Create Story'
});//最简单的解决办法就是 完全不碰数组 不碰props 单独渲染Cretea Story
return (
<ul>
{stories.map(story => (
<li key={story.id}>
{story.label}
</li>
))}
<li>Create Strory</li>
</ul>
//第二个办法就是创建一个新数组 保持mutation 发生在局部
let store=stories.slice();//不要影响原数组 记住哪些操作会影响原数组 非常有用 sort push pop reverse会改变 filter map会返回一个新的
);
}
将UI看成树
- 组件会嵌套,React是如何跟踪的,都是建模成树
- UI树:是项目和UI的关系模型,DOM和CSSOM
- 渲染树:组件的一个主要特性是能够由其他组件组合而成 从根节点父组件指向子组件
- Html在哪里呢?
React是跨平台的UI框架 React渲染树 仅由React组件构成,同样可以渲染到移动平台或者桌面设备
- 顶级组件和叶子组件
顶级组件是离根组件最近的组件,它们影响其下所有组件的渲染性能,通常包含最多复杂性。叶子组件位于树的底部,没有子组件,通常会频繁重新渲染。
- 模块依赖树
当拆分组件和逻辑到不同的文件中时,模块依赖树中的每个节点都是一个模块,每个分支代表该模块中的 import 语句。
- 模块依赖树对生产环境构建React应用非常有效,Bundler捆绑所有必要Js模块给客户端使用
摘要
- 树是表示实体之间关系的常见方式,它们经常用于建模 UI。
- 渲染树表示单次渲染中 React 组件之间的嵌套关系。
- 使用条件渲染,渲染树可能会在不同的渲染过程中发生变化。使用不同的属性值,组件可能会渲染不同的子组件。
- 渲染树有助于识别顶级组件和叶子组件。顶级组件会影响其下所有组件的渲染性能,而叶子组件通常会频繁重新渲染。识别它们有助于理解和调试渲染性能问题。
- 依赖树表示 React 应用程序中的模块依赖关系。
- 构建工具使用依赖树来捆绑必要的代码以部署应用程序。
- 依赖树有助于调试大型捆绑包带来的渲染速度过慢的问题,以及发现哪些捆绑代码可以被优化。