React概述
一、React概述
1.1、前端三大主流框架
- Angular.js:出来较早的前端框架,学习曲线比较陡,NG1学起来比较麻烦,NG2 ~ NG5开始,进行了一系列的改革,也提供了组件化开发的概念;从NG2开始,也支持使用TS(TypeScript)进行编程;
- Vue.js:最火(关注的人比较多)的一门前端框架,它是中国人开发的,对我我们来说,文档要友好一些;
- React.js:最流行(用的人比较多)的一门框架,因为它的设计很优秀;
1.2、简介
- React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架都不满意,就决定自己写一套,用来架设 Instagram(照片交友) 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
- 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。
- 版本号的命名说明
- React的版本号从0.14直接跳到15,移动了其中的小数点,希望借此提升这个迅猛发展的项目的认可度。
- React目前最新版本是18.2.0版本,相比较之前的16、17版本,其实改动很小。
1.3、官网
-
英文官网: https://reactjs.org/
-
强烈建议,查看英文官网,中文官网和英文官网会存在内部不同步的现象
1.3、React与Vue的对比
1.3.1、组件化方面
-
什么是模块化
含义:对外提供特定功能的js程序,一般就是一个js文件(一个js文件就是一个模块)。
为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂,把一些可复用的代码,抽离为单个模块。
作用:复用js,简化js的编写,提高js运行效率,便于项目的维护和开发。
==是从代码的角度来进行分析的==
-
什么是组件化:
含义:用来实现局部功能效果的代码和资源的集合(html/css/js/image等等) 。
为什么要拆成组件:一个界面的功能很复杂,把一些可复用的UI元素,抽离为单独的组件。
作用:复用编码, 简化项目编码, 提高运行效率,便于项目的维护和开发。
==是从 UI 界面的角度 来进行分析的==
-
组件化的好处:随着项目规模的增大,手里的组件越来越多;很方便就能把现有的组件,拼接为一个完整的页面;
-
Vue是如何实现组件化的: 通过 .vue 文件,来创建对应的组件,其中一个组件包含三部分:
- template 结构
- script 行为
- style 样式
-
React如何实现组件化:大家注意,React中有组件化的概念,但是,并没有像vue这样的组件模板文件;React中,一切都是以JS来表现的;因此要学习React,JS要合格;ES6 和 ES7 (async 和 await) 要会用;
1.3.2、开发团队方面
- React是由FaceBook前端官方团队进行维护和更新的;因此,React的维护开发团队,技术实力比较雄厚;
- Vue:第一版,主要是有作者 尤雨溪 专门进行维护的,当 Vue更新到 2.x 版本后,也有了一个以 尤雨溪 为主导的开源小团队,进行相关的开发和维护;
1.3.3、社区方面
- 在社区方面,React由于诞生的较早,所以社区比较强大,一些常见的问题、坑、最优解决方案,文档、博客在社区中都是可以很方便就能找到的;
- Vue是近两年才火起来的,所以,它的社区相对于React来说,要小一些,可能有的一些坑,没人踩过;
1.3.4、移动APP开发体验方面
- Vue,结合 Weex 这门技术,提供了 迁移到 移动端App开发的体验(Weex,阿里巴巴出品,一些成功的案例都是阿里系的项目)
- React,结合 ReactNative,也提供了无缝迁移到 移动App的开发体验(RN用的最多,也是最火最流行的);
1.4、React特点
- 采用组件化模式,声明式编码,提高开发效率及组件复用率
- 声明式编程:只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面。
- 在React Native中可以使用React语法进行移动端开发
- 使用虚拟DOM + 优秀的Diffing算法,尽量减少与真实DOM的交互。
- 多平台适配
- 2013年,React发布之初主要是开发Web页面;
- 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(虽然目前Flutter非常火爆,但是还是有很多公司在使用 ReactNative);
- 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序;(VR也会是一个火爆的应用场景);
1.5、为什么要学习React
- React设计很优秀,一切基于JS并且实现了组件化开发的思想;
- 开发团队实力强悍,不必担心断更的情况;
- 社区强大,很多问题都能找到对应的解决方案;
- 提供了无缝转到 ReactNative 上的开发体验,让我们技术能力得到了拓展;增强了我们的核心竞争力;
- 很多企业中,前端项目的技术选型采用的是React.js;
1.6、React高效的原因
- 使用虚拟(virtual)DOM, 不总是直接操作页面真实DOM。
- DOM Diffing算法,最小化页面重绘。
二、React中两个核心的概念
2.1、虚拟DOM
-
DOM的本质是什么:
浏览器中的概念,用JS对象来表示 页面上的元素,并提供了操作 DOM 对象的API;
-
什么是React中的虚拟DOM:
是框架中的概念,是程序员 用JS对象来模拟 页面上的 DOM 和 DOM嵌套;
-
为什么要实现虚拟DOM(虚拟DOM的目的):
为了实现页面中, DOM 元素的高效更新。
-
DOM和虚拟DOM的区别:
DOM: 浏览器中,提供的概念;用JS对象,表示页面上的元素,并提供了操作元素的API;
虚拟DOM: 是框架中的概念;是程序员手动用JS对象来模拟DOM元素和嵌套关系;
==本质: 用JS对象,来模拟DOM元素和嵌套关系;==
==目的: 就是为了实现页面元素的高效更新;==

2.2、Diff算法
-
tree diff:
新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;
-
component diff:
在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;
如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新;
如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上; -
element diff:
在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;

三、HelloWorld入门
3.1、效果演示

3.2、环境准备
需要引入React相关的js库,一共有3个:
-
react.js:
React核心库,专门用于创建组件和虚拟DOM的,同时组件的生命周期都在这个库中。
-
react-dom.js:
提供操作DOM的react扩展库。即要将创建的虚拟DOM渲染到页面上,就是
ReactDOM.render()。 -
babel.min.js:
解析JSX语法代码转为JS代码的库。
说明一:
- 对于Vue来说,我们只是依赖一个vue.js文件即可,但是react居然要依赖三个包。
- 其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情;
- 在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里;
说明二:为什么要进行拆分呢?原因就是react-native
- react包中包含了react web和react-native所共同拥有的核心代码。
- react-dom针对web和native所完成的事情不同:
- web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
- native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。
3.3、创建虚拟DOM的两种方式
3.3.1、环境说明
- React 版本使用的是最新版:18.2.0版本。
- 采用的是VS Code开发环境开发
- VS Code 快速创建html模板快捷键是:
新建好html页面之后,默认是空的,快速创建模板是:输入!+tab。
3.3.2、jsx方式
3.3.2.1、创建html页面
准备好“容器“,并创建好容器,将来使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中。
习惯上,在Vue中,容器的id叫 app,在React中,容器的id叫 root。
<!-- 创建容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
3.3.2.2、引入核心库
==需要注意引入js库的顺序,不能乱。==
<!-- 引入react核心库 -->
<script src="../lib/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="../lib/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script src="../lib/babel.min.js"></script>
3.3.2.3、创建虚拟DOM并渲染
==注意两点:==
- script的type必须是:
text/babel - 创建虚拟DOM的时候,一定不能加单引号,因为它本身其实不是标签,而是jsx。
<script type="text/babel">
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染到根节点上。
root.render(<h2>Hello World</h2>)
</script>
注意,在 React18版本之前,代码是这样的:
<script type="text/babel"> // 1、创建虚拟DOM,注意:一定不能加单引号,因为它不是字符串,而是jsx。 const VDOM = <h1>Hello World</h1> // 2、渲染虚拟DOM到页面上。 ReactDOM.render(VDOM, document.getElementById("test")); </script>
3.3.2.4、完整代码如下
<!DOCTYPE html>
<html lang="en">
<body>
<!-- 创建容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
<!-- 添加依赖 -->
<!-- 依赖三个包 -->
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染到根节点上。
root.render(<h2>Hello World</h2>)
</script>
</body>
</html>
3.3.2.5、打开测试

3.3.2.6、总结
- ReactDOM.createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个根中。
- root.render函数: 负责渲染
- render() 函数的参数是 ==HTML元素或者一个组件== 。
3.3.3、纯JS方式(一般不用)
3.3.3.1、创建html页面
准备好“容器“,并创建好容器,将来使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中。
<!-- 创建容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
3.3.3.2、引入核心库
==需要注意引入js库的顺序,不能乱,并且可以不用引入babel。==
<!-- 引入react核心库 -->
<script src="../lib/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="../lib/react-dom.development.js"></script>
3.3.3.3、创建虚拟DOM并渲染
==注意一点:==
- script的type必须是:
text/javascript - 创建虚拟DOM的时候,使用的是
React.createElement语法。
<script type="text/javascript">
// 1、创建虚拟DOM
// 第一个参数: 字符串类型的参数,表示要创建的标签的名称
// 第二个参数: 对象类型的参数, 表示 创建的元素的属性节点,如果没有属性可以用null表示
// 第三个参数: 子节点
const VDOM = React.createElement("h2", null, "Hello World");
// 2、根据指定的容器创建一个根节点DOM
// >> 参数1: 表示要渲染的虚拟DOM对象
// >> 参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象
const root = ReactDOM.createRoot(document.getElementById("root"));
// 3、渲染虚拟DOM到页面上
root.render(VDOM);
</script>
3.3.3.4、完整代码如下
<!DOCTYPE html>
<html lang="en">
<body>
<!-- 创建容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
<!-- 添加依赖 -->
<!-- 依赖两个包 -->
<!-- 引入react核心库 -->
<script src="../lib/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="../lib/react-dom.development.js"></script>
<script type="text/javascript">
// 1、创建虚拟DOM
// 第一个参数: 字符串类型的参数,表示要创建的标签的名称
// 第二个参数: 对象类型的参数, 表示 创建的元素的属性节点,如果没有属性可以用null表示
// 第三个参数: 子节点
const VDOM = React.createElement("h2", null, "Hello World");
// 2、根据指定的容器创建一个根节点DOM
// >> 参数1: 表示要渲染的虚拟DOM对象
// >> 参数2: 指定容器,注意:这里不能直接放 容器元素的Id字符串,需要放一个容器的DOM对象
const root = ReactDOM.createRoot(document.getElementById("root"));
// 3、渲染虚拟DOM到页面上
root.render(VDOM);
</script>
</body>
</html>
3.3.3.5、打开测试

3.3.3.6、总结
==其实,虽然我们使用jsx语法的形式创建虚拟DOM,但是本质上React依然内部先转换成了 createElement 形式。==
3.4、虚拟DOM与真实DOM
-
React提供了一些API来创建一种”特别” 的一般js对象
# 创建一个简单的虚拟DOM对象,本质是Object类型的对象 const VDOM = React.createElement('标签名', 属性对象 , 标签体); -
虚拟DOM是React内部在用。
-
虚拟DOM对象最终都会被React转换为真实的DOM,呈现在页面上。
-
我们编码时基本只需要操作react的虚拟DOM相关数据,react会转换为真实DOM变化而更新。
四、为什么纯js方式不用呢?
4.1、效果演示

4.2、纯js方式
<!DOCTYPE html>
<html lang="en">
<body>
<!-- 创建容器,来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
<!-- 添加依赖 -->
<!-- 依赖两个包 -->
<!-- 引入react核心库 -->
<script src="../lib/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script src="../lib/react-dom.development.js"></script>
<script type="text/javascript">
// 1、创建虚拟DOM
const VSpan = React.createElement("span", null, "Hello,React");
const VDOM = React.createElement("h1", { id: "title" }, VSpan);
// 2、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.getElementById("root"));
// 3、渲染虚拟DOM到页面上
root.render(VDOM);
</script>
</body>
</html>
4.3、jsx方式
<!DOCTYPE html>
<html lang="en">
<body>
<!-- 创建容器,将来,使用 React 创建的虚拟DOM元素,都会被渲染到这个指定的容器中 -->
<div id="root"></div>
<!-- 添加依赖 -->
<!-- 依赖三个包 -->
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM
const VDOM = (
<h1 id="title">
<span>Hello,React</span>
</h1>
);
// 3、渲染虚拟DOM到页面上
root.render(VDOM)
</script>
</body>
</html>
4.4、总结
通过上述案例分析发现,使用纯js去创建虚拟DOM的时候,如果DOM节点存在嵌套,会发现此种方式会非常繁琐,故纯js方式一般不用,以后知道有此种方式即可,不做重点掌握。
五、HelloWorld案例升级
5.1、需求
5.1.1、效果

5.1.2、说明
点击【改变文本】按钮,页面上的文本内容由”Hello World“ 修改为 ”Hello React“ 。
5.2、代码实现1
5.2.1、开发步骤
- “Hello World” 文本内容不是固定写死的,需要用变量定义出来。
- 按钮需要绑定点击事件,需要定义事件处理程序,本质是一个函数。
- 点击按钮修改变量的值,并重新渲染。
- 在 jsx 代码中去引用变量需要使用"{}"包裹起来。
5.2.2、代码
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 将文本定义成变量,这样就可以控制修改了
let message = 'Hello World';
// 定义函数,按钮的事件处理程序
function clickHandle(){
// >> 修改数据
message = 'Hello React';
// >> 重新渲染
root.render((
<div>
<h2>{message}</h2>
<button onClick={clickHandle}>改变文本</button>
</div>
));
}
// 2、创建虚拟DOM并渲染
root.render((
<div>
<h2>{message}</h2>
<button onClick={clickHandle}>改变文本</button>
</div>
));
</script>
</body>
</html>
5.2.3、代码改进
发现:“root.render()” 重新渲染是重复性的代码,可以将该代码用函数封装起来。代码优化如下:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 将文本定义成变量,这样就可以控制修改了
let message = 'Hello World';
// 第一次渲染
rootRender();
// 定义函数,按钮的事件处理程序
function clickHandle(){
// >> 修改数据
message = 'Hello React';
// >> 重新渲染
rootRender();
}
// 2、创建虚拟DOM并渲染
function rootRender(){
root.render((
<div>
<h2>{message}</h2>
<button onClick={clickHandle}>改变文本</button>
</div>
));
}
</script>
</body>
</html>
5.3、组件化开发
5.3.1、分析
通过上例代码发现,代码的逻辑其实主要是分为三部分:
- 定义数据变量
- let message = 'Hello World';
- 对数据变量的操作
- function clickHandle()
- 对页面的渲染
- rootRender()
对于这三部分操作应该统一的封装起来,可以封装成一个组件。在React中,组件包含有两种形式:
- 类式组件
- 函数式组件
5.3.2、步骤一:渲染页面
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
// 定义组件
class MyComponent extends React.Component{
render(){
let message = 'HelloWorld';
return (
<div>
<h2>{message}</h2>
<button>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html>
- root.render()函数, 参数是一个HTML元素或者一个组件;
- 可以将之前的业务逻辑封装到一个组件中,然后传入到render函数中的第一个参数。
- 这个组件名称叫MyComponent,其实就是一个类,类名必须大写,即组件的名称是必须大写的,因为小写会被认为是HTML元素,并且需要继承自React.Component。
- 重写render()函数,该函数返回的jsx内容,就是之后React会帮助我们渲染的内容。
5.3.3、步骤二:数据依赖
现在的问题:数据在哪里定义?
很明显,对于数据的定义是不能直接定义在render()函数中的,因为这样的话,意味着render()函数再次执行数据会重置。实际上在React中,在组件中的数据,我们可以分成两类:
- 参与界面更新的数据:当数据改变时,需要更新组件渲染的内容;
- 不参与界面更新的数据:当数据改变时,不需要更新将组建渲染的内容。
参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中。
- 我们可以通过在构造函数中 this.state = {定义的数据};
- 当我们的数据发生变化时,我们可以调用 this.setState 来更新数据,并且通知React进行update操作。
- 在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面。
代码如下:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class MyComponent extends React.Component{
constructor(){
super();
// 定义数据
this.state = {
message: 'HelloWorld'
};
}
render(){
return (
<div>
<h2>{this.state.message}</h2>
<button>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html
5.3.4、步骤三:绑定事件
为按钮绑定点击事件,代码如下:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class MyComponent extends React.Component{
constructor(){
super();
// 定义数据
this.state = {
message: 'HelloWorld'
};
}
// 事件绑定
clickHandle(){
// 修改数据
this.setState({
message: 'Hello React'
});
}
render(){
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.clickHandle}>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html>
点击按钮之后发现报错,报错原因查看控制台:

很奇怪,居然是undefined。不可思议,那该如何理解呢?
- 因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说是button对象);
- 这次因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象;
- 那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined;
那么此时就引申出来另一个问题:事件绑定中的this的问题。即:在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向的是谁呢?
因为我们在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的this。
解决办法:我们就需要在传入函数时,给这个函数直接绑定this。形如这样的写法:
<button onClick={this.clickHandle.bind(this)}>改变文本</button>
代码如下所示:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class MyComponent extends React.Component{
constructor(){
super();
// 定义数据
this.state = {
message: 'HelloWorld'
};
}
// 事件绑定
clickHandle(){
// 修改数据
this.setState({
message: 'Hello React'
});
}
render(){
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.clickHandle.bind(this)}>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html>
5.3.5、优化改进1
在给按钮绑定事件的同时就直接通过bind绑定this解决了问题,但是如果存在的按钮有很多,假如都需要绑定事件,那么都采取类似这样的写法,会发现很麻烦,怎么办呢?解决办法是:可以在 constructor 构造函数中就通过 bind 函数去绑定this。
代码如下:
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class MyComponent extends React.Component{
constructor(){
super();
// 定义数据
this.state = {
message: 'HelloWorld'
};
// 直接绑定this
this.btnClickHandle = this.clickHandle.bind(this);
}
// 事件绑定
clickHandle(){
// 修改数据
this.setState({
message: 'Hello React'
});
}
render(){
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.btnClickHandle}>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html>
注意的是:给按钮绑定事件时,绑定的是构造函数中动态添加的属性 btnClickHandle。
5.3.6、优化改进2
使用箭头函数。
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class MyComponent extends React.Component{
constructor(){
super();
// 定义数据
this.state = {
message: 'HelloWorld'
};
}
// 事件绑定
clickHandle = () => {
// 修改数据
this.setState({
message: 'Hello React'
});
}
render(){
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.clickHandle}>改变文本</button>
</div>
)
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<MyComponent/>);
</script>
</body>
</html>
5.3.7、setState方法
修改state状态数据,需要使用 setState 方法。该方法实际上了做了两件事:
- 将state中状态数据值修改
- 自动重新执行render函数,使页面重新渲染
六、人员列表案例
6.1、需求
6.1.1、效果

6.1.2、说明
- 需要使用到循环操作
- 同时在渲染每个子节点的同时,还需要为每个节点指定 key 。
6.2、代码实现方式一
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class Person extends React.Component{
constructor(){
super();
this.state = {
persons: [
'张三',
'李四',
'王五',
'周芷若'
]
}
}
render(){
// 构造一个 li 的集合
let liElements = [];
for(let i = 0 ; i < this.state.persons.length ; i++){
// 获取当前的人员
let person = this.state.persons[i];
// 构造 li
let liElement = <li key={i}>{person}</li>
// 添加到数组中
liElements.push(liElement);
}
return (
<div>
<ul>
{liElements}
</ul>
</div>
);
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<Person/>);
</script>
</body>
</html>
- 要永远记住,render函数中编写的是 jsx 代码。
- 在渲染子节点的同时,还需要为每个子节点指定 key。
6.3、代码实现方式二
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class Person extends React.Component{
constructor(){
super();
this.state = {
persons: [
'张三',
'李四',
'王五',
'周芷若'
]
}
}
render(){
// 构造一个 li 的集合
let liElements = this.state.persons.map(item=>{
return <li key={item}>{item}</li>
});
return (
<div>
<ul>
{liElements}
</ul>
</div>
);
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<Person/>);
</script>
</body>
</html>
使用 map 方式。
七、计数器案例
7.1、需求

7.2、代码实现
<!DOCTYPE html>
<html lang="en">
<body>
<div id="root"></div>
<script src="../lib/react.development.js"></script>
<script src="../lib/react-dom.development.js"></script>
<script src="../lib/babel.min.js"></script>
<script type="text/babel">
class Counter extends React.Component{
constructor(){
super();
this.state = {
number: 1
}
}
add(){
this.setState({
number: this.state.number + 1
});
}
substract(){
this.setState({
number: this.state.number - 1
});
}
render(){
return (
<div>
<button onClick={this.add.bind(this)}>+1</button>
{this.state.number}
<button onClick={this.substract.bind(this)}>-1</button>
</div>
);
}
}
// 1、根据指定的容器创建一个根节点DOM
const root = ReactDOM.createRoot(document.querySelector("#root"));
// 2、创建虚拟DOM并渲染
root.render(<Counter />);
</script>
</body>
</html>
- 重点要注意事件回调函数中的 this ,需要使用 bind 。




