React - 核心概念
hello world
1 | ReactDOM.render( |
JSX (语法糖)
1 | const element = <h1>Hello, world!</h1>; |
- JSX 仅仅只是 React.createElement(component, props, …children) 函数的语法糖
- jsx包含
视图与事件
- 可嵌入表达式
- 可包含多个子元素
- 防止注入攻击:渲染前,输入内容会被转义(字符串)
1 | const title = response.potentiallyMaliciousInput; |
Babel
把JSX转义成React.createElement()
函数调用
1 | const element = ( |
1 | // 预先检查,实际内部(简化过的结构) |
深入 JSX
指定 React 元素类型
- React 必须在作用域内, React 库也必须包含在 JSX 代码作用域内
1 | // 如果不使用 JavaScript 打包工具而是直接通过 <script> 标签加载 React,必须将 React 挂载到全局变量中 |
- 在 JSX 类型中使用
点语法
1 | import React from 'react'; |
- 自定义组件必须以大写字母开头
- 在运行时选择类型,不能将通用表达式作为 React 元素类型
1 | const components = { |
JSX 中的 Props
- js表达式
1 | <MyComponent foo={1 + 2 + 3 + 4}/> |
- 字符串字面量
1 | <MyComponent message="hello world"/> |
- Props 默认值为 True
1 | <MyTextBox autocomplete/> |
- 属性展开传递
1 | function App2() { |
JSX 中的子元素
- JSX 表达式作为特定属性
props.children
传递给外层组件 - 允许由多个 JSX 元素组成
1 | // 字符串字面量 与 JSX 子元素 可以一起使用 |
1 | // React 组件也能返回存储在数组中的一组元素 |
- js表达式作为子元素
1 | function Hello(props) { |
- 函数作为子元素
1 | // 调用子元素回调 numTimes 次,来重复生成组件 |
- 布尔类型、Null、Undefined 不会渲染
1 | // 渲染结果相同 |
- 依据特定条件渲染 React 元素
1 | <div> |
- ⚠️ 有一些 false 值,如数字 0,仍会被 React 渲染
1 | // {props.messages.length}会被渲染 |
元素渲染
- React元素是
开销极小
的普通对象
- React Dom负责
更新Dom
与 React元素保持一致
元素渲染为DOM
- React构建的应用只有单一的根DOM节点,已有应用可以包含多个独立根DOM节点
更新已渲染的元素
- React元素是
不可变对象
,一旦创建无法修改它的子元素或属性 - 一个元素代表了特定时刻的UI
React 只更新它需要更新的部分
- React DOM 会将元素和它的子元素与它们之前的状态进行比较,只会进行
必要的更新DOM
组件 & Props
1 | function Welcome(props) { |
渲染组件
- React元素可以是
DOM标签
或自定义组件
- 自定义组件:JSX接收的属性转化为单个对象传递 称为
props对象
组合组件
- 每个新的React应用的顶层都是App组件 不包含集成情况
提取组件
- 嵌套复杂难维护应提取组件,命名从组件自身角度命名,不依赖上下文调用
Props 的只读性
- 所有 React 组件都必须像
纯函数
一样,它们的 props 不能被更改
1 | // 不会更改自己的入参 |
State & 生命周期
- State 与 props 类似,state 是
私有的
,完全受控于当前组件
函数组件转化为class组件
1 | class Clock extends React.Component { |
class组件添加局部的state
1 | class Clock extends React.Component { |
生命周期方法添加到 Class 中
1 | // 1. Clock 被传给 ReactDOM.render(),react调用组件的构造函数 |
State 的更新可能是异步的
- 不要直接修改 State
1 | // 可换 => |
State 的更新会被合并(浅合并)
- 调用setState()时,React 会把新对象合并到当前的 state
- state 是局部的或是封装的, 除拥有或设置它的组件,其它组件无法访问
- React 可能把多个 setState() 合并成一个调用
1 | constructor(props) |
数据是向下流动的
- 任何的 state 总是属于特定的组件,从 state 派生的任何数据或 UI 只能影响树中
低于
它们的组件
1 | // 自定义组件传递参数 |
事件处理
- React事件命名 小驼峰
- 使用JSX语法 传入
事件处理函数
不是字符串
1 | // 传统HTML |
- 不能通过返回 false 阻止默认行为, 必须使用
preventDefault
1 | // |
- 一般不需要使用 addEventListener 监听DOM元素,在元素初始渲染时添加监听器
- class 的方法默认不会绑定
this
, 所以调用函数时 this 为 undefined
1 | class Toggle extends React.Component { |
除bind绑定this外,还可以
- 如果使用实验性的public class fields 语法,可以使用 class
fields 绑定回调函数,Create React App 默认启用此语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}- ⚠️ 回调中使用箭头函数:每次渲染组件都会创建不同的回调函数,如果作为props传给子组件,组件会重复渲染,所以在构造器中绑定
1
2
3
4
5
6handleClick() {
console.log('this is:', this);
}
<button onClick={() => this.handleClick()}>
Click me
</button>向事件处理程序传递参数
1
2
3
4
5// 箭头函数:事件对象必须显式传递
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
// bind绑定:事件对象、更多参数会隐式传递
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>- 如果使用实验性的public class fields 语法,可以使用 class
条件渲染
- JSX中嵌入任意表达式:if 语句、与运算符 &&、三目运算
- 隐藏组件 render 直接返回 null(不会影响组件的生命周期)
列表 & Key
- key 为确定标识,帮助 React 识别元素的改变
- 没有确定的id, 万不得已可以使用index:列表顺序变化,性能变差,还可能引起组件状态问题
- 数组元素使用的 key 在兄弟节点之间必须唯一
1 | function Blog(props) { |
表单
受控组件
- HTML:表单元素自己维护 state,根据输入更新。 React:可变状态保存在组件的 state 属性中 只能通过
setState()
更新 - 受控组件:渲染表单的 React 组件控制输入过程中表单发生的操作(输入的值始终由 React 的 state 驱动)
1 |
|
- 处理多个input输入,添加name属性,处理函数根据 event.target.name 的值选择要执行的操作
- 在受控组件上指定 value 的 prop 会阻止用户更改输入
状态提升
- 任何可变数据应当只有一个相对应的
唯一数据源
- 多个组件共用state,提升至它们最近共同父组件中,依靠自上而下的数据流,而不是在组件之间同步 state
1 | // 例:两个输入框同时改变值 |
组件 vs 继承
- 包含关系:JSX 嵌套,子组件为任意组件
- 组件可以接受任意 props,包括基本数据类型、React 元素、函数
1 | // 将任何东西作为 props 进行传递 |
- 继承
- 并没有发现需要使用继承来构建组件层次的情况
- 如果想要在组件间复用非 UI 的功能,可以将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它
React 哲学
- 根据UI划分为组件层级
- 创建React静态版本
- 确定应用所需的 state 的最小集合
- 确定 state 放置的位置
- 找到根据这个 state 渲染的所有组件
- 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)
- 该共同所有者组件或者比它层级更高的组件应该拥有该 state
- 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置
- 添加反向数据流
- 父组件传入