React OnePage
React 是由Facebook研发的、用于解决 UI 复杂度的开源JavaScript 库,目前由 React 联合社区维护。
它不是框架,只是为了解决 UI 复杂度而诞生的一个库,并不是 MVVM MVC
React 的特点
- 轻量:React 的开发版所有源码(包含注释)仅 3000 多行
- 原生:所有的 React 的代码都是用原生 JS 书写而成的,不依赖其他任何库
- 易扩展:React 对代码的封装程度较低,也没有过多的使用魔法,所以 React 中的很多功能都可以扩展。
- 不依赖宿主环境:React 只依赖原生 JS 语言,不依赖任何其他东西,包括运行环境。因此,它可以被轻松的移植到浏览器、桌面应用、移动端。
- 渐近式:React 并非框架,对整个工程没有强制约束力。这对与那些已存在的工程,可以逐步的将其改造为 React,而不需要全盘重写。
- 单向数据流:所有的数据自顶而下的流动
- 用 JS 代码声明界面
- 组件化
Hello World
1<!-- React的核心库,与宿主环境无关 -->
2<script
3 crossorigin
4 src="https://unpkg.com/react@16/umd/react.development.js"
5></script>
6<!-- 依赖核心库,将核心的功能与页面结合 -->
7<script
8 crossorigin
9 src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
10></script>
11crossorigin跨域。script本身可以跨域,为何还要加呢?报错会显示详细信息
React.createElement
创建一个React 元素,称作虚拟 DOM,本质上是一个对象
- 参数 1:元素类型,如果是字符串,一个普通的 HTML 元素
- 参数 2:元素的属性,一个对象
- 后续参数:元素的子节点
最原始的写法:
1// 创建一个span元素
2var span = React.createElement("span", {}, "一个span元素");
3// 创建一个h1元素
4创建一个H1元素;
5var h1 = React.createElement(
6 "h1",
7 {
8 title: "第一个React元素",
9 },
10 "Hello",
11 "World",
12 span
13);
14ReactDOM.render(h1, document.getElementById("root"));
React 本质:createElement 创建一个对象,把这些对象形成一种结构,最终交给 ReactDOM 渲染到页面上
JSX
JS 的扩展语法,需要使用 babel 进行转义。
1<!-- 编译JSX -->
2<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
3<script type="text/babel">
4 // 创建一个span元素
5 var span = <span>一个span元素</span>;
6 // 创建一个H1元素
7 var h1 = (
8 <h1 title="第一个React元素">
9 Hello World <span>一个span元素</span>
10 </h1>
11 );
12 console.log(h1);
13 // 等价于直接写 最终都会转成React.createElement来执行
14 // ReactDOM.render(<h1 title="第一个React元素">Hello World <span>一个span元素</span></h1>, document.getElementById("root"));
15</script>
emmet 配置:emmet 语法书写代码
1"javascript": "javascriptreact"
ESLint:代码风格检查:合适的时候提出警告
ES7 React/Redux/GraphQL/React-Native snippets:快速代码编写
React Developer Tools
什么是 JSX
1import React from "react";
2import ReactDOM from "react-dom";
3const a = 1234,
4 b = 2345;
5
6const div = (
7 <h1>
8 {a} * {b} = {a * b};
9 </h1>
10);
11// 底层实现上
12// const div = React.createElement("div",{},`${a}*${b} = ${a*b}`)
13ReactDOM.render(div, document.getElementById("root"));
元素的不可变性
- 虽然 JSX 元素是一个对象,但是该对象中的所有属性不可更改
1div.props.num = 2;
2div.props.title = "sac";
3// 不能重新设置的原因:Object.freeze了
- 如果确实需要更改元素的属性,需要重新创建 JSX 元素
1import React from "react";
2import ReactDOM from "react-dom";
3let num = 0;
4setInterval(() => {
5 num++;
6 const div = <div title="asa">{num}</div>;
7 ReactDOM.render(div, document.getElementById("root"));
8}, 1000);
DOM 优化:效率很高,只变动 div 的内容
- 函数组件
返回一个 React 元素
- 类组件
必须继承 React.Component
必须提供 render 函数,用于渲染组件
组件的属性
- 对于函数组件,属性会作为一个对象的属性,传递给函数的参数
- 对于类组件,属性会作为一个对象的属性,传递给构造函数的参数
注意:组件的属性,应该使用小驼峰命名法
组件无法改变自身的属性。(只读)
之前学习的 React 元素,本质上,就是一个组件(内置组件) (都只读)
React 中的哲学:数据属于谁,谁才有权力改动(父组件也无权改)
React 中的数据,自顶而下流动
组件状态
组件状态:组件可以自行维护的数据
组件状态仅在类组件中有效
状态(state),本质上是类组件的一个属性,是一个对象
状态初始化
方法 1
1class Demo {
2 constructor(props) {
3 super(props);
4 this.state = {
5 left: this.props.left,
6 };
7 }
8}
方法 2
1state = {
2 left: this.props.number,
3};
状态的变化
不能直接改变状态:因为 React 无法监控到状态发生了变化
必须使用 this.setState({})改变状态
一旦调用了 this.setState,会导致当前组件重新渲染
组件中的数据
- props:该数据是由组件的使用者传递的数据,所有权不属于组件自身,因此组件无法改变该数据
- state:该数据是由组件自身创建的,所有权属于组件自身,因此组件有权改变该数据。只有组件自身可以改
事件
在 React 中,组件的事件,本质上就是一个属性
如果没有特殊处理,在事件处理函数中,this 指向 undefined
- 使用 bind 函数,绑定 this
- 使用箭头函数
深入认识 setState
setState,它对状态的改变,可能是异步的
如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,否则是同步
如果遇到某个事件中,需要同步调用多次,需要使用函数的方式得到最新状态
最佳实践:
- 把所有的 setState 当作是异步的
- 永远不要信任 setState 调用之后的状态
- 如果要使用改变之后的状态,需要使用回调函数(setState 的第二个参数)
- 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(setState 的第一个函数)
React 会对异步的 setState 进行优化,将多次 setState 进行合并(将多次状态改变完成后,再统一对 state 进行改变,然后触发 render)
生命周期
生命周期:组件从诞生到销毁会经历一系列的过程,该过程就叫做生命周期。React 在组件的生命周期中提供了一系列的钩子函数(类似于事件),可以让开发者在函数中注入代码,这些代码会在适当的时候运行。
生命周期仅存在于类组件中,函数组件每次调用都是重新运行函数,旧的组件即刻被销毁
旧版生命周期
React < 16.0.0
- constructor
- 同一个组件对象只会创建一次
- 不能在第一次挂载到页面之前,调用 setState,为了避免问题,构造函数中严禁使用 setState
- 因为 setState 会导致重新渲染,在挂载之前,没必要重新渲染
-
componentWillMount
- 正常情况下,和构造函数一样,它只会运行一次
- 可以使用 setState,但是为了避免 bug,不允许使用,因为在某些特殊情况下,该函数可能被调用多次
-
render
- 返回一个虚拟 DOM,会被挂载到虚拟 DOM 树中,最终渲染到页面的真实 DOM 中
- render 可能不只运行一次,只要需要重新渲染,就会重新运行
- 严禁使用 setState,因为可能会导致无限递归渲染
-
componentDidMount
- 只会执行一次
- 可以使用 setState
- 通常情况下,会将网络请求、启动计时器等一开始需要的操作,书写到该函数中
-
组件进入活跃状态
-
componentWillReceiveProps
- 即将接收新的属性值
- 参数为新的属性对象
- 该函数可能会导致一些 bug,所以不推荐使用
-
shouldComponentUpdate
- 指示 React 是否要重新渲染该组件,通过返回 true 和 false 来指定
- 默认情况下,会直接返回 true
- 属性直接被赋值了,不一定要值变化
-
componentWillUpdate
- 组件即将被重新渲染
-
componentDidUpdate
- 往往在该函数中使用 dom 操作,改变元素
-
componentWillUnmount
-
通常在该函数中销毁一些组件依赖的资源,比如计时器

新版生命周期
React >= 16.0.0
React 官方认为,某个数据的来源必须是单一的。要么来自属性,要么来自状态。
-
getDerivedStateFromProps
- 通过参数可以获取新的属性和状态
- 该函数是静态的
- 该函数的返回值会覆盖掉组件状态
- 该函数几乎是没有什么用
-
getSnapshotBeforeUpdate
- 运行时间:真实的 DOM 构建完成,但还未实际渲染到页面中。
- 在该函数中,通常用于实现一些附加的 dom 操作
- 该函数的返回值,会作为 componentDidUpdate 的第三个参数
- 配合 componentDidUpdate 使用

传递元素内容
内置组件:div、h1、p
1<div>asdfafasfafasdfasdf</div>
index.js
1ReactDOM.render(<New html={<h1>sss</h1>} />, document.getElementById("root"));
new.js
1export default function New(props) {
2 return <div className="comp">{props.html}</div>;
3}
如果给自定义组件传递元素内容,则 React 会将元素内容作为 children 属性传递过去。
传递多个:
1export default function Comp(props) {
2 return (
3 <div>
4 {props.children}
5 {props.content1}
6 {props.content2}
7 </div>
8 );
9}
10
11function Test() {
12 return (
13 <div>
14 <Comp content1={<h1>demo</h1>} content2={<h2>demo</h2>}>
15 <div>children</div>
16 </Comp>
17 </div>
18 );
19}
表单
受控组件和非受控组件
受控组件:组件的使用者,有能力完全控制该组件的行为和内容。通常情况下,受控组件往往没有自身的状态,其内容完全收到属性的控制。
非受控组件:组件的使用者,没有能力控制该组件的行为和内容,组件的行为和内容完全自行控制。
表单组件,默认情况下是非受控组件,一旦设置了表单组件的 value 属性,则其变为受控组件(单选和多选框需要设置 checked)
属性默认值 和 类型检查
属性默认值
之前是用混入解决的:Object.assign({},defaultProps,props)
通过一个静态属性defaultProps告知 react 属性默认值
函数本身的属性:函数也是对象
混合完成时间:
- 函数组件:调用函数之前就完成了
- 类组件:运行构造函数之前完成
1// 函数组件
2
3export default function Comp(props) {
4 console.log(props)// 已经完成了混合
5 return (
6 <div>Comp</div>
7 )
8}
9Comp.defaultProps = {
10 a: 1,
11 b: 2
12}
13
14/// 类组件
15export default class Comp extends Component {
16 // static defaultProps = {
17 // a: 1,
18 // b: 2
19 // }
20 render() {
21 return (
22 <div>index</div>
23 )
24 }
25}
26// 类的本质也是函数
27Comp.defaultProps = {
28 a: 1
29}
属性类型检查
使用库:prop-types
对组件使用静态属性propTypes告知 react 如何检查属性
如果不按照类型传递属性的话,报警告,不影响代码执行。(只在开发阶段报警告)
可以不传递,但是一旦传递,必须正确
1PropTypes.any://任意类型 通常用在列出所有属性,方便阅读;可以设置必填
2PropTypes.array://数组类型
3PropTypes.bool://布尔类型
4PropTypes.func://函数类型 事件
5PropTypes.number://数字类型
6PropTypes.object://对象类型
7PropTypes.string://字符串类型
8PropTypes.symbol://符号类型
9
10PropTypes.node:
11//任何可以被渲染的内容,字符串、数字、React元素。如果传递null undefined,没有报错,因为没有进行非空验证
12PropTypes.element://react元素
13PropTypes.elementType://react元素类型
14PropTypes.instanceOf(构造函数)://必须是指定构造函数的实例
15PropTypes.oneOf([xxx, xxx])://枚举
16PropTypes.oneOfType([xxx, xxx]); //属性类型必须是数组中的其中一个
17PropTypes.arrayOf(PropTypes.XXX)://必须是某一类型组成的数组
18PropTypes.objectOf(PropTypes.XXX)://对象由某一类型的值组成
19PropTypes.shape(对象): //属性必须是对象,并且满足指定的对象要求
20PropTypes.exact({...})://和shape一样,只是exact要求对象必须精确匹配传递的数据
21
22//自定义属性检查,如果有错误,返回错误对象即可
23属性: function(props, propName, componentName) {
24}
25export default class Comp extends Component {
26 static propTypes = {
27 score: function (props, propName, componentName) {
28 const val = props[propName];
29 // 必填
30 if (val === undefined || val === null) return new Error('Invalid')
31 // 必须是数字
32 if (typeof val !== 'number') return new Error('Invalid')
33 }
34 }
35 render() {
36 return (
37 <div>
38 {this.props.score}
39 </div>
40 )
41 }
42}
HOC 高阶组件
HOF:Higher-Order Function, 高阶函数,以函数作为参数,并返回一个函数
HOC: Higher-Order Component, 高阶组件,以组件作为参数,并返回一个组件
通常,可以利用 HOC 实现横切关注点。
举例:20 个组件,每个组件在创建组件和销毁组件时,需要作日志记录
20 个组件,它们需要显示一些内容,得到的数据结构完全一致
注意
- 不要在 render 中使用高阶组件。在 render 内部的话重新创建,失去状态,浪费效率。在 render 外部则使用的是同一个类。
- 不要在高阶组件内部更改传入的组件(防止混乱)
ref
场景:希望直接使用 dom 元素中的某个方法,或者希望直接使用自定义组件中的某个方法
- ref 作用于内置的 html 组件,得到的将是真实的 dom 对象
- ref 作用于类组件,得到的将是类的实例
- ref 不能作用于函数组件
1import React, { Component } from "react";
2
3/**
4 * 类组件
5 */
6class A extends Component {
7 method() {
8 console.log("调用了A组件的方法");
9 }
10 render() {
11 return <h1>组件A</h1>;
12 }
13}
14
15/**
16 * 内置html组件
17 */
18export default class Comp extends Component {
19 handleClick = () => {
20 // console.log(this)
21 this.refs.txt.focus();
22 this.refs.compA.method();
23 };
24 render() {
25 return (
26 <div>
27 <input type="text" ref="txt" />
28 <A ref="compA" />
29 <button onClick={this.handleClick}>聚焦</button>
30 </div>
31 );
32 }
33}
以上:ref 不再推荐使用字符串赋值,字符串赋值的方式将来可能会被移出。(效率低)
目前,ref 推荐使用对象或者是函数
对象
通过 React.createRef 函数创建
1import React, { Component } from "react";
2
3export default class Comp extends Component {
4 constructor(props) {
5 super(props);
6 /*
7 方法2:这样创建对象也可以
8 this.txt = {
9 current: null
10 } */
11 // 方法1 createRef
12 this.txt = React.createRef();
13 console.log(this.txt); //{current: null}
14 // 第一次渲染的时候会给current赋值
15 // current: input
16 }
17 handleClick = () => {
18 // console.log(this)
19 this.txt.current.focus();
20 };
21 render() {
22 return (
23 <div>
24 <input ref={this.txt} type="txt" />
25 <button onClick={this.handleClick}>聚焦</button>
26 </div>
27 );
28 }
29}
函数
函数的调用时间:
- componentDidMount 的时候会调用该函数
- 在 componentDidMount 事件中可以使用 ref
- 如果 ref 的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在 componentDidUpdate 之前
- 旧的函数被调用时,传递 null
- 新的函数被调用时,传递对象
- 如果 ref 所在的组件被卸载,会调用函数
谨慎使用 ref
能够使用属性和状态进行控制,就不要使用 ref。
- 调用真实的 DOM 对象中的方法
- 某个时候需要调用类组件的方法
Ref 转发
forwardRef
forwardRef 方法:
- 参数,传递的是函数组件,不能是类组件,并且,函数组件需要有第二个参数来得到 ref
- 返回值,返回一个新的组件
1import React, { Component } from "react";
2
3function A(props, ref) {
4 // console.log(props, ref)
5 /* return <h1>组件A
6 <span>{props.word}</span>
7 </h1> */
8 return (
9 <h1 ref={ref}>
10 组件A
11 <span>{props.word}</span>
12 </h1>
13 );
14}
15// 传递函数组件A,得到一个新组件NewA
16const NewA = React.forwardRef(A); //一旦经过了这个操作,ref代表的就由函数组件自己控制了
17
18export default class App extends Component {
19 ARef = React.createRef();
20 // componentDidMount() {
21 // console.log(this.ARef)//null 啥都得不到,让我自己去控制。依靠第二个参数了
22 // }
23
24 render() {
25 return (
26 <div>
27 {/* <A ref={this.ARef} /> */}
28 {/* 木得:this.ARef.current:h1即组件内部的根元素 */}
29 <NewA ref={this.ARef} word="aa" />
30 </div>
31 );
32 }
33}
类组件咋办呢?
方法 1
1import React, { Component } from "react";
2
3/**
4 * ref:就是一个对象
5 * ref:{
6 * current:null
7 * }
8 */
9class A extends React.Component {
10 render() {
11 return (
12 <h1 ref={this.props.ref1}>
13 组件A
14 <span>{this.props.words}</span>
15 </h1>
16 );
17 }
18}
19export default class App extends Component {
20 ARef = React.createRef();
21 componentDidMount() {
22 console.log(this.ARef);
23 }
24 render() {
25 return (
26 <div>
27 <A ref1={this.ARef} words="aa" />
28 </div>
29 );
30 }
31}
方法 2
1import React, { Component } from "react";
2
3class A extends React.Component {
4 render() {
5 return (
6 <h1 ref={this.props.abc}>
7 组件A
8 <span>{this.props.words}</span>
9 </h1>
10 );
11 }
12}
13/**
14 * 函数包装一下
15 */
16const NewA = React.forwardRef((props, ref) => {
17 return <A {...props} abc={ref} />;
18});
19export default class App extends Component {
20 ARef = React.createRef();
21 componentDidMount() {
22 console.log(this.ARef);
23 }
24
25 render() {
26 return (
27 <div>
28 <NewA ref={this.ARef} words="aa" />
29 </div>
30 );
31 }
32}
高阶组件
1import React from "react";
2import { A } from "./components/Comp";
3import withLog from "./HOC/withLog";
4let AComp = withLog(A);
5
6export default class App extends React.Component {
7 myRef = React.createRef();
8 componentDidMount() {
9 console.log(this.myRef); //logwrapper
10 }
11 render() {
12 return (
13 <div>
14 {/* ref:代表了日志记录了,但是我想代表A */}
15 <AComp ref={this.myRef} />
16 </div>
17 );
18 }
19}
改装 withLog.js
1import React from "react";
2
3/**
4 * 高阶组件
5 * @param {*} comp 组件
6 */
7export default function withLog(Comp) {
8 class LogWrapper extends React.Component {
9 componentDidMount() {
10 console.log(`日志:组件${Comp.name}被创建了!${Date.now()}`);
11 }
12 componentWillUnmount() {
13 console.log(`日志:组件${Comp.name}被销毁了!${Date.now()}`);
14 }
15 render() {
16 //正常的属性
17 //forwardRef代表要转发的ref {current:null}
18 const { forwardRef, ...rest } = this.props;
19 return (
20 <>
21 <Comp ref={forwardRef} {...rest} /> 最终,current指向他
22 </>
23 );
24 }
25 }
26
27 return React.forwardRef((props, ref) => {
28 return <LogWrapper {...props} forwardRef={ref} />;
29 });
30}
PureComponent
纯组件:用于避免不必要的渲染(运行 render),从而提高效率
优化:如果一个组件的属性和状态,都没有发生变化,该组件的重新渲染是没有必要的
PureComponent 是一个组件,如果某个组件继承自该组件,则该组件的 shouldComponentUpdate 会进行优化,对属性和状态进行钱比较。相等就不会重新渲染
注意场景:改动之前的数组,地址不会变化,浅比较会认为没发生变化。所以尽量不改动原数组,应该创建新数组
- 为了效率,尽量用纯组件
- 不要改变之前的状态,永远是创建新的状态覆盖之前的状态(Immutable 不可变对象)
1// 加入要改变对象的某个值,不要改变原对象
2obj:{
3 ...this.state.obj,
4 b:500
5}
6// 或者
7
8Object.assign({},this.state.obj, {b:500})
- 有一个第三方库,Immutable.js 专门用于制作不可变对象
函数组件没有生命周期,每次需要重新调用函数,要防止重新调用函数,使用 React.memo 函数制作纯组件
1export default React.memo(Task); //高阶组件 套了一层类组件
实现原理
1function memo(FuncComp) {
2 return class Memo extends PureComponent {
3 render() {
4 // return <FuncComp {...this.props}/>
5 return <>{FuncComp(this.props)}</>;
6 }
7 };
8}
render props
Render Props – React
https://www.bilibili.com/video/BV13k4y117UG/?spm_id_from=333.788.recommend_more_video.1&vd_source=5ca956a1f37d0ed72cd1c453c15a3c03
有时候,某些组件的各种功能及其处理逻辑几乎完全相同,只是显示的界面不一样,建议下面的方式认选其一来解决重复代码的问题(横切关注点)
- render props
- 某个组件,需要某个属性
- 该属性是一个函数,函数的返回值用于渲染
- 函数的参数会传递为需要的数据
- 注意纯组件的属性(尽量避免每次传递的 render props 的地址不一致,应该把函数提出来)
- 通常该属性的名字叫做 render
- HOC
Portals
插槽:将一个 React 元素渲染到指定的 DOM 容器中
ReactDOM.createPortal(React 元素, 真实的 DOM 容器),该函数返回一个 React 元素
1import React from "react";
2import ReactDOM from "react-dom";
3function ChildA() {
4 return ReactDOM.createPortal(
5 <div className="child-a">
6 <h1>ChildA</h1>
7 <ChildB />
8 </div>,
9 document.querySelector(".modal")
10 );
11}
12function ChildB() {
13 return (
14 <div className="child-b">
15 <h1>ChildB</h1>
16 </div>
17 );
18}
19export default function App() {
20 return (
21 <div className="app">
22 <h1>App</h1>
23 <ChildA />
24 </div>
25 );
26}
现象:真实的 DOM 结构变成了代码那样,但是组件结构变化。说明 React 虚拟 DOM 树和真实 DOM 树可以有差异
注意事件冒泡
- React 中的事件是包装过的
- 它的事件冒泡是根据虚拟 DOM 树(组件结构)来冒泡的,与真实的 DOM 树无关。
1import React from "react";
2import ReactDOM from "react-dom";
3function ChildA() {
4 return ReactDOM.createPortal(
5 <div
6 className="child-a"
7 style={{
8 marginTop: 200,
9 }}
10 >
11 <h1>ChildA</h1>
12 <ChildB />
13 </div>,
14 document.querySelector(".modal")
15 );
16}
17function ChildB() {
18 return (
19 <div className="child-b">
20 <h1>ChildB</h1>
21 </div>
22 );
23}
24export default function App() {
25 return (
26 <div
27 className="app"
28 onClick={(e) => {
29 console.log("App被点击了", e.target);
30 }}
31 >
32 <h1>App</h1>
33 <ChildA />
34 </div>
35 );
36}
错误边界
默认情况下,若一个组件在渲染期间(render)发生错误,会导致整个组件树全部被卸载
错误边界:是一个组件,该组件会捕获到渲染期间(render)子组件发生的错误,并有能力阻止错误继续传播
让某个组件捕获错误
- 编写生命周期函数 getDerivedStateFromError
- 静态函数
- 运行时间点:渲染子组件的过程中,发生错误之后,在更新页面之前
- 注意:只有子组件发生错误,才会运行该函数。自己发生错误处理不了
- 该函数返回一个对象,React 会将该对象的属性覆盖掉当前组件的 state
- 参数:错误对象
- 通常,该函数用于改变状态
- 编写生命周期函数 componentDidCatch
- 实例方法
- 运行时间点:渲染子组件的过程中,发生错误,更新页面之后,由于其运行时间点比较靠后,因此不太会在该函数中改变状态
- 通常,该函数用于记录错误消息
细节
某些错误,错误边界组件无法捕获
- 自身的错误
- 异步的错误
- 事件中的错误
这些错误,需要用 try catch 处理
总结:仅处理渲染子组件期间的同步错误
React 中的事件
这里的事件:React 内置的 DOM 组件中的事件
- 给 document 注册事件
- 几乎所有的元素的事件处理,均在 document 的事件中处理
- 一些不冒泡的事件,是直接在元素上监听
- 一些 document 上面没有的事件,直接在元素上监听
- 在 document 的事件处理,React 会根据虚拟 DOM 树的完成事件函数的调用
- React 的事件参数,并非真实的 DOM 事件参数,是 React 合成的一个对象,该对象类似于真实 DOM 的事件参数
- stopPropagation,阻止事件在虚拟 DOM 树中冒泡
- nativeEvent,可以得到真实的 DOM 事件对象
- 为了提高执行效率,React 使用事件对象池来处理事件对象
注意事项
- 如果给真实的 DOM 注册事件,阻止了事件冒泡,则会导致 react 的相应事件无法触发
- 如果给真实的 DOM 注册事件,事件会先于 React 事件运行
- 通过 React 的事件中阻止事件冒泡,无法阻止真实的 DOM 事件冒泡
- 可以通过 nativeEvent.stopImmediatePropagation(),阻止 document 上剩余事件的执行
- 在事件处理程序中,不要异步的使用事件对象,如果一定要使用,需要调用 persist 函数