React18 (二) React元素 和 JSX

1.React元素

1.1 React.createElement()

React.createElement(type, [props], [...children]) - 用来创建React元素,React元素无法修改

1.2 ReactDOM.createRoot()

createRoot(container[, options]) - 用来创建React的根容器,容器用来放置React元素

1.3 root.render()

root.render(element) 
当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 DOM 差分算法(DOM diffing algorithm)进行高效的更新。
不会修改容器节点(只会修改容器的子节点)。可以在不覆盖现有子节点的情况下,将组件插入已有的 DOM 节点中。

演示代码 

需要下载和导入依赖 https://unpkg.com/react@18.0.0/umd/react.development.js,https://unpkg.com/react-dom@18.0.0/umd/react-dom.development.js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React01</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>

</head>

<body>
    <div id="root1"></div>
    <div id="root2"></div>

    <button id="trigger">Show Button</button>
    <script>
        //create <div> by DOM
        const div = document.createElement('div');
        console.log(div);
        div.innerText = "this is a <div>";
        const root1 = document.getElementById("root1");
        root1.appendChild(div);


        // create <div> by React
        // params: 1.element name 2.element attribute 3.the child element of element
        const div2 = React.createElement("div", {}, "this is a <div> create by React");
        console.log(div2);
        // create Root element
        const root2 = ReactDOM.createRoot(document.getElementById("root2"));
        // render div to root element
        root2.render(div2);


        /** update DOM by React, replace all element when need update */
        const btn = document.getElementById("trigger");
        btn.addEventListener('click', () => {

            const btn = React.createElement("button", { id: "btn2", className: "btn2", onClick: () => { confirm("go ahead!!!") } }, "New Button");

            const div2 = React.createElement("div", {}, "this is a <div> create by React",btn);

            // render div to root element
            root2.render(div2);
        });

    </script>
</body>

</html>

2.JSX

JSX是JavaScript 的语法扩展,JSX 使得我们可以以类似于HTML的形式去使用JS。JSX便是React中声明式编程的体现方式。声明式编程,简单理解就是以结果为导向的编程。使用JSX将我们所期望的网页结构编写出来,然后React再根据JSX自动生成JS代码。所以我们所编写的JSX代码,最终都会转换为以调用React.createElement()创建元素的代码。由于JSX最终需要转换为JS代码执行,所以浏览器并不能正常识别JSX,所以当我们在浏览器中直接使用JSX时,还必须引入babel来完成对代码的编译。

演示代码

需要进入babel依赖,https://unpkg.com/babel-standalone@6/babel.min.js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React01 JSX</title>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>

</head>

<body>
    <div id="root1"></div>
    <script type="text/babel">

        const title = "use babel";
        const content = "just demo jsx, and need import bable, setup script type='text/bable'";

        const fn = () => "call funtion and get the return value";

        const div = <div className="jsx">
            <header style={{ fontSize: '20px' }}>{title}</header>
            <p>{content}</p>
            <textarea>{fn()}</textarea>
        </div>;
        const root = ReactDOM.createRoot(document.getElementById("root1"));
        root.render(div);
    </script>
</body>

</html>

JSX的注意事项:

1.html标签不要加引号
2.有且只有一个根标签
3.html标签小写开头,React组件大写开头
4.可以使用{}插入JS表达式。(表达式:有返回值的语句。JSX也是表达式)
5.属性正常写(class使用className,style必须用{})
6.标签必须正常闭合
7.布尔类型、Null 以及 Undefined 将会忽略

代码地址: https://github.com/showkawa/react18-ZeroToOne/blob/main/react01/jsx.html

3. 虚拟DOM

当我们通过React操作DOM时,比如通过React.createElement()创建元素时。我们所创建的元素并不是真正的DOM对象而是React元素,React元素是React应用的最小组成部分。
与浏览器的 DOM 元素不同,React 元素就是一个普通的JS对象,且创建的开销极小。 React元素不是DOM对象,那为什么可以被添加到页面中去呢?实际上每个React元素都会有一个对应的DOM元素,对React元素的所有操作,最终都会转换为对DOM元素操作,
也就是所谓的虚拟DOM。要理解虚拟DOM,我们需要先了解它的作用。虚拟DOM就好像我们和真实DOM之间的一个桥梁。有了虚拟DOM,使得我们无需去操作真实的DOM元素,
只需要对React元素进行操作,所有操作最终都会映射到真实的DOM元素上。 这不是有点多余吗?直接操作DOM不好吗?为什么要多此一举呢?原因其实很多,这里简单举几个出来。 首先,虚拟DOM简化了DOM操作。凡是用过DOM的都知道Web API到底有多复杂,各种方法,各种属性,数不胜数。查询的、修改的、删除的、添加的等等等等。
然而在虚拟DOM将所有的操作都简化为了一种,那就是创建!React元素是不可变对象,一旦创建就不可更改。要修改元素的唯一方式就是创建一个新的元素去替换旧的元素,
看起来虽然简单粗暴,实则却是简化了DOM的操作。 其次,解决DOM的兼容性问题。DOM的兼容性是一个历史悠久的问题,如果使用原生DOM,总有一些API会遇到兼容性的问题。使用虚拟DOM就完美的避开了这些问题,
所有的操作都是在虚拟DOM上进行的,而虚拟DOM是没有兼容问题的,至于原生DOM是否兼容就不需要我们操心了,全都交给React吧! 最后,我们手动操作DOM时,由于无法完全掌握全局DOM情况,经常会出现不必要的DOM操作,比如,本来只需要修改一个子节点,但却不小心修改了父节点,
导致所有的子节点都被修改。效果呈现上可能没有什么问题,但是性能上确实千差万别,修改一个节点和修改多个节点对于系统的消耗可是完全不同的。
然而在虚拟DOM中,引入了diff算法,React元素在更新时会通过diff算法和之前的元素进行比较,然后只会对DOM做必要的更新来呈现结果。简单来说,
就是拿新建的元素和旧的元素进行比较,只对发生变化的部分对DOM进行更新,减少DOM的操作,从而提升了性能。

4.渲染列表

演示代码

前面的代码演示没有UI效果,如果不想在demo代码操心UI可以直接引入 bootstrap css库,https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React01 DOM</title>
    <link href="css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
    <script src="js/babel.min.js"></script>
</head>

<body>
    <button id="btn" type="button" class="btn btn-primary">click me</button>
    <div id="root"></div>
    <script type="text/babel">
        const root = ReactDOM.createRoot(document.getElementById("root"));
        document.querySelector("#btn").onclick = function () {
            const data = ["Apache", "Gateway", "ELFK", "Redis", "Kafka"];
            const list = <ul>{data.map((item, index) => <li key={item}>{index+1} - {item}</li>)}</ul>;
            root.render(list);
        };
    </script>
</body>
</html>

上面的需要渲染出来的<li>标签都有设置属性key,在同一级列表中元素都没有key的话,当你打开控制台时你一定会看到一行红色的内容,它大概内容是:Warning: Each child in a list should have a unique “key” prop.

Warning表示一个警告,警告表示它并不是一个特别严重的错误,但你最好把它处理了,所以当你在React项目中看到这个玩意的时候一定一定要想办法去除掉它。

为什么会报出这个警告呢?事情要往前回溯一下,我们已经知道React是通过虚拟DOM来操作元素的,而React为了提升操作的性能,在DOM发生变化时只会去修改那些发生了变化的元素,这样就大大的减少了DOM操作从而提升了页面渲染的速度。

React怎么知道哪些元素发生变化呢?其实也不难,React每次渲染都会生成一个由React元素构成的树(当然这棵树也对应着一课DOM元素构成的树,但是这里不太重要),React每次重新渲染都会生成一个新的React元素树,在页面刷新前,React会通过内部的diff算法对两个树中的React元素进行比较,并且找到那些发生变化的元素,并将他们的变化在真实的DOM元素上体现出来。

添加了key属性以后警告就消失了,设置key还有一些要求:

1.key必须在当前列表的元素中是唯一的
2.一个元素的key最好是固定的

如果我使用了元素的索引(index)作为key来使用,但这有什么用吗?没用!因为index是根据元素位置的改变而改变的,当我们在前边插入一个新元素时,所有元素的顺序都会一起改变,那么它和React中按顺序比较有什么区别吗?没有区别!而且还麻烦了,唯一的作用就是去除了警告。所以我们开发的时候偶尔也会使用索引作为key,但前提是元素的顺序不会发生变化,除此之外不要用索引做key。

posted @ 2022-07-31 11:48  Brian_Huang  阅读(419)  评论(0编辑  收藏  举报