React概述

2025年5月27日 Mr 焦 149

一、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、官网

  1. 英文官网: https://reactjs.org/

  2. 中文官网: https://react.docschina.org/

  3. 强烈建议,查看英文官网,中文官网和英文官网会存在内部不同步的现象

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元素和嵌套关系;==
    ​ ==目的: 就是为了实现页面元素的高效更新;==

001

2.2、Diff算法

  • tree diff:

    ​ 新旧两棵DOM树,逐层对比的过程,就是 Tree Diff; 当整颗DOM逐层对比完毕,则所有需要被按需更新的元素,必然能够找到;

  • component diff:

    ​ 在进行Tree Diff的时候,每一层中,组件级别的对比,叫做 Component Diff;

    ​ 如果对比前后,组件的类型相同,则暂时认为此组件不需要被更新;
    ​ 如果对比前后,组件类型不同,则需要移除旧组件,创建新组件,并追加到页面上;

  • element diff:

    ​ 在进行组件对比的时候,如果两个组件类型相同,则需要进行 元素级别的对比,这叫做 Element Diff;

002

三、HelloWorld入门

3.1、效果演示

001

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>

002

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、打开测试

003

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、打开测试

004

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、效果演示

005

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、效果

006

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>
  1. root.render()函数, 参数是一个HTML元素或者一个组件;
  2. 可以将之前的业务逻辑封装到一个组件中,然后传入到render函数中的第一个参数。
  3. 这个组件名称叫MyComponent,其实就是一个类,类名必须大写,即组件的名称是必须大写的,因为小写会被认为是HTML元素,并且需要继承自React.Component。
  4. 重写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>

​ 点击按钮之后发现报错,报错原因查看控制台:

007

​ 很奇怪,居然是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、效果

008

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>
  1. 要永远记住,render函数中编写的是 jsx 代码。
  2. 在渲染子节点的同时,还需要为每个子节点指定 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、需求

009

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>
  1. 重点要注意事件回调函数中的 this ,需要使用 bind 。
分类:
标签:
版权属于Mr 焦
本文链接:https://www.mtsws.cn/post-3.html
评论
暂无评论数据
相关推荐
react-native-reanimated用法清单
以下是 react-native-reanimated 的用法清单和核心文档说明,涵盖主要功能、API 和使用示例: 一、核心概念 工作线程:动画在 UI 线程执行(非 JS 线程),避免卡顿 共享值(Shared Values):动画的驱动数据(代替 Animated.Value) 动画修饰器:定义动画行为(如 withTiming, withSpring...
Mr 焦 2952025年5月29日
Linux 系统-常见目录
常见目录 Linux 将整个文件系统看作一棵树,这棵树的树根叫做根文件系统,用 / 表示。 常用的系统文件目录 目录语义描述/rootRoot Directories系统管理员的主目录/home/usernameHome Directories普通用户的主目录/binUser Binaries供所有用户使用的完成 基本维护任务的命令/sbinSystem B...
Mr 焦 2752025年5月30日 Linux
js数据类型
数据类型 JavaScript 是一种 弱类型语言 或者说 动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。 这也意味着你可以使用同个相同名称的变量保存不同类型的数据: p var foo = 42; /p p // foo is a Number now /p p /p p var foo...
Mr 焦 2212025年5月27日 js
浏览器架构
浏览器架构 计算机核心元素 为了了解浏览器运行的环境,我们需要了解几个计算机部件以及它们的作用。 CPU 第一个需要了解的计算机部件是 中央处理器(Central Processing Unit),或简称为 CPU。CPU 可以看作是计算机的大脑。一个 CPU 核心如图中的办公人员,可以逐一解决很多不同任务。它可以在解决从数学到艺术一切任务的同时还知道如何响...
Mr 焦 2432025年5月27日
Node.js中Joi的详细用法
Joi 在 Node.js 中的详细用法 Joi 是一个强大的 JavaScript 对象模式验证库,常用于 Node.js 应用中验证和转换数据。下面详细介绍 Joi 的用法。 安装 npm install joi 基本用法 1. 基本验证 const Joi = require( joi ); // 定义 schema const schema = Jo...
Mr 焦 2002025年6月8日