1. React 与 JSX

一、基础知识

  • 基于 React 18.x
  • 官网:https://zh-hans.react.dev
  • 开发 React 必须依赖的三个库:
    • react:包含react(web和react-native)所必须的核心代码
    • react-dom:react渲染在不同平台所需要的核心代码
      • web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
      • native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,IOS中的UIButton)。
    • babel:将jsx转换成React代码的工具
      • 又名 Babel.js。 是目前前端使用非常广泛的编译器、转移器。
      • 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。
      • 那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
  • React的依赖引入:这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息
    	<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    	<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    	<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    
  • 下面的代码示例如没有特殊说明均需要引用上边三个文件

二、JSX 知识

(一) 认识 JSX

  • JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
  • 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
  • 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind);
  • React认为渲染逻辑本质上与其他UI逻辑存在内在耦合,他们之间是密不可分,所以React没有将html,js,css分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component);
  • JSX的书写规范:
    • JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素(或者使用后面我们学习的Fragment);
    • 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
    • JSX中的标签可以是单标签,也可以是双标签;注意:如果是单标签,必须以/>结尾;
    <div id="root"></div>
    <script src="https://unpkg.com/react@18/umd/react.development.js"  crossorigin ></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin ></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    
    <!-- type="text/babel" 作用是可以让 babel 解析 jsx 的语法 -->
    <script type="text/babel">
      let msg = 'Hello world';
      // jsx 语法
      // 它不是一段字符串(因为没有使用引号包裹);
      const element = (
        <div>
          <div>{msg}</div>
          <div>固定显示</div>
        </div>
      );
    
      // 渲染 jsx 语法代表的元素
      // ReactDOM 来源于react-dom.js 文件
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(element);
    </script>
    

(二) JSX 注释

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const element = (
    <div>
      {/* 这是一条注释 */}
      <div>一条信息</div>
    </div>
  );
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

(三) JSX 嵌入变量作为子元素

【1】 Number、String、Array 类型

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const num = 1;
  const str = '2';
  const arr1 = [1, 2, 3];
  // 报错
  // const arr2 = [{ a: 1 }, { b: 2 }];
  const arr3 = [[1,2], [3,4]];

  const element = (
    <div>
      {/* Number */}
      <div>{1}</div>
      <div>{num}</div>
      {/* String */}
      <div>{'2'}</div>
      <div>{str}</div>
      {/* Array */}
      <div>{['a', 'b', 'c']}</div>
      <div>{arr1}</div>
      {/* 1234 */}
      <div>{arr3}</div>
    </div>
  );
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

【2】 null、undefined、Boolean 类型

  • 当变量是null、undefined、Boolean类型时,在浏览器上将显示空白;
  • 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
  • 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const und = undefined;
  const nul = null;
  const truth = true;

  const element = (
    <div>
      {/* undefined */}
      {/* 显示空白 */}
      <div>{undefined}</div>
      <div>{und}</div>
      {/* null */}
      {/* 显示空白 */}
      <div>{null}</div>
      <div>{nul}</div>
      {/* boolean */}
      {/* 显示空白 */}
      <div>{truth}</div>
      <div>{false}</div>
      {/* 显示字符串:false */}
      <div>{false + ''}</div>
    </div>
  );
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

【3】 Object 对象类型

  • Object对象类型不能作为子元素(not valid as a React child)

    <div id="root"></div>
    <!-- 需要引入react文件 -->
    <script type="text/babel">
      const obj = { name: '张三', age: 52 };
    
      const element = (
        <div>
          {/*报错:Uncaught Error: Objects are not valid as a React child */}
          {/*<div>{obj}</div>*/}
          <div>{obj.name}</div>
          <div>{obj.age}</div>
        </div>
      );
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(element);
    </script>
    

(四) JSX 嵌入表达式

  • 运算表达式

  • 三元运算符

  • 执行一个函数

    <div id="root"></div>
    <!-- 需要引入react文件 -->
    <script type="text/babel">
       let a = true;
       const arr = ['张三', '里斯', '王五'];
    
       function getNames() {
         const newArr = arr.map((item, index) => {
           return <li key={index}>{item + index}</li>;
         });
    
         return newArr;
       }
    
       const element = (
         <div>
           <div>{1 + 1}</div>
           <div>{a ? 3 : 4}</div>
    
           {/*注意:这里使用的是map,即实际插入的是map处理后的返回的数组*/}
           <ul>
             {arr.map((item) => (
               <li key={item}>{item}</li>
             ))}
           </ul>
    
           <ul>{getNames()}</ul>
         </div>
       );
       const root = ReactDOM.createRoot(document.getElementById('root'));
       root.render(element);
     </script>
    

(五) JSX 绑定属性

【1】固定属性

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const id = 5
  const title = '我是title';
  const imgSrc = './img/a.png';
  const imgAlt = '图片备注';

  const element = (
    <div>
      <div data-id={id}>携带额外的信息</div>
      <div title={title}>这里有title</div>
      {/*单标签元素必须以 /> 结尾*/}
      <img src={imgSrc} alt={imgAlt} />
      
    </div>
  );
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

【2】class

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const className = 'active';
  const isActive = true;
  // 已有的css类上添加新的类
  const className2 = `white blue ${isActive ? className : ''}`;
  const className3 = ['white', 'blue'];
  if (isActive) {
    className3.push(className);
  }

  const element = (
    <div>
      {/*会被警告: Invalid DOM property `class`*/}
      <div class={className}>被警告</div>
      {/*正确做法*/}
      <div className="abc">普通</div>
      <div className={className}>正确</div>
      <div className={className2}>字符串</div>
      <div className={className3.join(' ')}>数组</div>
    </div>
  );
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

【3】style

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  const styleObj = {
    color: 'blue',
    fontWeight: 'bold',
    // 'font-size': '22px' // 警告:Unsupported style property font-size
  };

  const element = (
    <div>
      {/*报错:The `style` prop expects a mapping from style properties to values, not a string.*/}
      {/*<div style="color: blue;">普通</div>*/}
      {/* 对象 */}
      <div style={{ color: 'red', fontSize: '20px' }}>对象1</div>
      <div style={styleObj}>对象2</div>
    </div>
  );

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(element);
</script>

(六) 组件化(类的方式封装组件)

  • 使用类方式封装一个组件

    • 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component
    • 组件必须实现 render 函数并返回的jsx内容,之后React会帮助我们渲染jsx内容
  • 参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中

    • 当我们的数据发生变化时,我们可以调用 this.setState 来更新数据,并且通知React 进行 update 操作;
    • 在进行 update 操作时,就会重新调用 render 函数,并且使用最新的数据,来渲染界面
    <div id="root"></div>
    <!-- 需要引入react文件 -->
    <script type="text/babel">
      // 1.类组件
      class App extends React.Component {
        constructor() {
          super();
    
          // 页面需要渲染的数据,放在 state 中且名字不可变
          this.state = {
            message: 'Hello World',
            active: true,
            styleObj: {
              color: 'blue',
              fontSize: '22px',
            },
          };
    
          setTimeout(() => {
            // 两秒之后更新message, 页面内容将会随着更新
            this.setState({
              message: 'Hello React',
            });
          }, 2000);
        }
    
        // 必须有, 调用该组件时就会调用该方法
        // 数据改变时,该方法将会重新被调用
        render() {
          const { active } = this.state;
          return (
            <div>
              <h2>{this.state.message}</h2>
              <p className={active ? 'active' : ''}>一段话</p>
              <p style={this.state.styleObj}>第二段话</p>
            </div>
          );
        }
      }
    
      // 将组件添加到页面
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(<App />);
    </script>
    

(七) JSX 事件绑定

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写;

  • 通过 {} 传入一个事件处理函数,这个函数会在事件发生时被执行;

    <div id="root"></div>
    <!-- 需要引入react文件 -->
    <script type="text/babel">
      class App extends React.Component {
        constructor() {
          super();
          this.state = {
            message: 'Hello World',
          };
        }
    
        render() {
          return (
            <div>
              <h2>{this.state.message}</h2>
              <button onClick={this.btnClick}>按钮</button>
            </div>
          );
        }
    
        btnClick() {
          console.log(this); // undefined
          console.log('按钮被执行');
        }
      }
    
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(<App />);
    </script>
    

【1】事件中 this

  • 由上一个案例中可以看到调用的事件中的 thisundefined

  • ES6 规定在 JavaScript 类中默认就是严格模式 ,严格模式下找不到 this 的指向将会是 undefined

    'use strict';
        
    /**
     * this 的四种绑定:
     * 1. 默认绑定 foo()
     * 2. 隐式绑定:被一个对象执行 obj.foo() -> obj
     * 3. 显式绑定:call/ apply/ bind 
     * 4. new 绑定:new Foo() -> 创建一个新对象,并且赋值给 this
    */
    class A {
      btnClick() {
        console.log(this);
      }
    }
    
    const a = new A();
    a.btnClick(); // A
    const btnClick2 = a.btnClick;
    btnClick2(); // undefined
    
    function foo() {
      console.log(this);
    }
    
    // 非严格模式下:window
    // 严格模式下:undefined
    foo();
    
  • 在上一个案例中 this 为 undefined 原因: btnClick 函数只是被绑定在button 中(this 没有隐式绑定为当前类),并没有直接调用,当 React 内部调用了btnClick函数时,直接执行了函数(默认绑定),该函数 中的 this 没有被绑定,所以是 undefined;

  • 如何解决this的问题呢?

    • 方案一:bind给btnClick显示绑定this
    • 方案二:使用 ES6 class fields 语法
    • 方案三:事件监听时传入箭头函数(个人推荐)
    <div id="root"></div>
    <!-- 需要引入react文件 -->
    <script type="text/babel">
      class App extends React.Component {
        constructor() {
          super();
          this.state = {
            message: 'Hello World',
            count: 1,
          };
    
          // 解决方案一:
          this.btn2 = this.increase.bind(this);
        }
    
        render() {
          const { count } = this.state;
          return (
            <div>
              <h2>
                {this.state.message}
                {count}
              </h2>
              {/*this没有指定:*/}
              <button onClick={this.btnClick}>按钮1</button>
    
              {/*解决方案一:*/}
              <button onClick={this.increase.bind(this)}>添加2</button>
              <button onClick={this.btn2}>添加2.1</button>
    
              {/*解决方案二:*/}
              <button onClick={this.decrease}>减少3</button>
    
              {/*解决方案三:直接传入箭头函数*/}
              {/*推荐*/}
              {/*方式一:*/}
              <button
                onClick={() => {
                  console.log(this);
                  this.setState({ count: this.state.count - 1 });
                }}
              >
                减少4
              </button>
              
              {/*方式二:*/}
              {/*可以和按钮1做个对比(隐式绑定this)*/}
              <button onClick={() => this.btnClick2()}>减少4.1</button>
            </div>
          );
        }
    
        btnClick() {
          console.log(this); // undefined
          console.log('按钮被执行');
        }
    
        increase() {
          console.log(this); // App
          this.setState({
            count: this.state.count + 1,
          });
        }
    
        // 解决方案二:将类属性设置为一个箭头函数
        decrease = () => {
          console.log(this); // App
          this.setState({
            count: this.state.count - 1,
          });
        };
    
        // 解决方案三:通过箭头函数调用该函数,可以和btnClick函数做个对比
        // 推荐
        btnClick2() {
          console.log(this); // App
          this.setState({
            count: this.state.count - 1,
          });
        }
      }
    
      const root = ReactDOM.createRoot(document.getElementById('root'));
      root.render(<App />);
    </script>
    

【2】事件中参数的传递

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        message: 'Hello World',
      };
    }

    btnClick(event) {
      // 被React 包装过的event:SyntheticBaseEvent
      console.log(event);
      // 按钮1:undefined
      // 按钮2:App
      console.log(this);
    }

    btnClick2(name, age, event) {
      console.log(name, age, event);
    }

    btnClick3(event, name, age) {
      console.log(event, name, age);
    }

    render() {
      return (
        <div>
          {/* 传递event */}
          <button onClick={this.btnClick}>按钮1</button>
          <button onClick={this.btnClick.bind(this)}>按钮2</button>
          <button onClick={(event) => this.btnClick(event)}>按钮3</button>
          <br />
          {/* 传递额外的参数 */}
          {/* 没有显示的传递event */}
          {/* 函数 foo('123') 传递的参数,会在 bind 绑定时传递参数的后边 */}
          <button onClick={this.btnClick2.bind(this, '张三', 15)}>
            按钮4
          </button>
          <button onClick={(event) => this.btnClick3(event, '里斯', 25)}>
            按钮5
          </button>
        </div>
      );
    }
  }

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(<App />);
</script>

(八) 条件判断

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
   class App extends React.Component {
     constructor() {
       super();
       this.state = {
         message: 'Hello World',
         isReady: false,
         friend: {
           name: '张三',
           age: 14,
         },
       };
     }

     render() {
       let { isReady, friend } = this.state;
       //方式一: 使用if语句
       let showElement;
       if (isReady) {
         showElement = <div>我准备好了!</div>;
       } else {
         showElement = <div>还没有准备好</div>;
       }

       return (
         <div>
           {showElement}
           {/*方式二:三元运算符*/}
           <div>{isReady ? <span>已经好了</span> : <span>没有好</span>}</div>
           {/*方式三:&&与运算符*/}
           <div>{friend && <span>姓名:{friend.name},年龄:{friend.age}</span> }</div>
         </div>
       );
     }
   }

   const root = ReactDOM.createRoot(document.getElementById('root'));
   root.render(<App />);
 </script>

(九) 列表渲染

【1】案例

<div id="root"></div>
<!-- 需要引入react文件 -->
<script type="text/babel">
   class App extends React.Component {
     constructor() {
       super();
       this.state = {
         students: [
           { id: '001', name: '张三', score: 50 },
           { id: '002', name: '里斯', score: 70 },
           { id: '003', name: '王五', score: 80 },
           { id: '004', name: '赵六', score: 90 },
         ],
       };
     }

     render() {
       const { students } = this.state;

       const studentInfos = students.map((item, index) => (
         <li key={item.id}>
           学号:{item.id},姓名:{item.name},成绩:{item.score}
           <button
             onClick={() => {
               this.delStu(index);
             }}
           >
             删除学生
           </button>
         </li>
       ));

       const betterInfos = students
         .filter((item) => item.score > 70)
         .map((item) => (
           <li key={item.id}>
             学号:{item.id},姓名:{item.name},成绩:{item.score}
           </li>
         ));

       return (
         <div>
           <h2>学生信息</h2>
           <ul>{studentInfos}</ul>
           <h2>成绩大于70的学生</h2>
           <ul>{betterInfos}</ul>
         </div>
       );
     }

     delStu(index) {
       // 不要直接操作 state 中的数据
       // 将需要操作的数据进行浅拷贝
       const newStudents = [...this.state.students];
       newStudents.splice(index, 1);
       // 更新数据
       this.setState({
         students: newStudents,
       });
     }
   }

   const root = ReactDOM.createRoot(document.getElementById('root'));
   root.render(<App />);
 </script>

【2】列表中的 key

  • key主要的作用是为了提高diff算法时的效率;
  • 必须给数组中的每一项都指定一个 key——它可以是字符串或数字的形式,只要能唯一标识出各个数组项就行
  • 一个精心选择的 key 值所能提供的信息远远不止于这个元素在数组中的位置。即使元素的位置在渲染的过程中发生了改变,它提供的 key 值也能让 React 在整个生命周期中一直认得它。
  • 直接把数组项的索引当作 key 值来用,实际上,如果你没有显式地指定 key 值,React 确实默认会这么做。但是数组项的顺序在插入、删除或者重新排序等操作中会发生改变,此时把索引顺序用作 key 值会产生一些微妙且令人困惑的 bug。
  • 请不要在运行过程中动态地产生 key,像是 key={Math.random()} 这种方式。这会导致每次重新渲染后的 key 值都不一样,从而使得所有的组件和 DOM 元素每次都要重新创建。这不仅会造成运行变慢的问题,更有可能导致用户输入的丢失。所以,使用能从给定数据中稳定取得的值才是明智的选择。

(十) JSX的原理

【1】React.createElement 函数

  • 实际上,jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖。所有的jsx最终都会被转换成React.createElement 的函数调用。

  • createElement需要传递三个参数:

    • 参数一:type
      • 当前ReactElement的类型;
      • 如果是标签元素,那么就使用字符串表示 ,如: “div”;
      • 如果是组件元素,那么就直接使用组件的名称;
    • 参数二:config
      • 所有jsx中的属性都在config中以对象的属性和值的形式存储;
      • 比如传入className作为元素的class;
    • 参数三:children
      • 存放在标签中的内容,以children数组的方式进行存储;
      • 当然,如果是多个元素呢?React内部有对它们进行处理,处理的源码在下方
  • 该 JSX 代码将会被 babel 转换为 React.createElement 函数形式(具体看 通过babel官网将 jsx 进行转换

    <div className="active" id="box">
      <span data-id="1" onClick={btnClick}><label>子元素1</label></span>
      <span data-id="2">{msg}</span>
    </div>
    
    /*#__PURE__*/ React.createElement(
      "div",
      {
        className: "active",
        id: "box"
      },
      /*#__PURE__*/ React.createElement(
        "span",
        {
          "data-id": "1",
          onClick: btnClick
        },
        /*#__PURE__*/ React.createElement("label", null, "\u5B50\u5143\u7D201")
      ),
      /*#__PURE__*/ React.createElement(
        "span",
        {
          "data-id": "2"
        },
        msg
      )
    );
    

【2】通过 babel 官网将 jsx 进行转换

  • babel 官网:https://babeljs.io/

  • 在官网中找到 Try it out 选项,并进行如下配置,即可看到
    在这里插入图片描述

  • 通过babel 官网转换过的 JSX 可以直接在 react 中使用

    <div id="root"></div>
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    
    <!-- 不需要引用babel文件 -->
    <!-- script 标签不需要type -->
    <script>
      class App extends React.Component {
        constructor() {
          super();
          this.state = {
            msg: 'hello',
          };
        }
    
        render() {
          const { msg } = this.state;
          let btnClick = () => {
            console.log('btnClick');
          };
    
          const element = /*#__PURE__*/ React.createElement(
            'div',
            {
              className: 'active',
              id: 'box',
            },
            /*#__PURE__*/ React.createElement(
              'span',
              {
                'data-id': '1',
                onClick: btnClick,
              },
              /*#__PURE__*/ React.createElement(
                'label',
                null,
                '\u5B50\u5143\u7D201'
              )
            ),
            /*#__PURE__*/ React.createElement(
              'span',
              {
                'data-id': '2',
              },
              msg
            )
          );
    
          return element;
        }
      }
    
      const root = ReactDOM.createRoot(document.getElementById('root'));
      // 注意这里的写法
      root.render(React.createElement(App, null));
    </script>
    

【3】创建虚拟DOM

  • React.createElement 最终创建出来一个 ReactElement 对象
  • React 利用 ReactElement 对象组成了一个JavaScript的对象树,即虚拟DOM(Virtual DOM)
  • React官方的说法:Virtual DOM 是一种编程理念。
    • 在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
    • 我们可以通过 ReactDOM.render 让虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation);
  • babel 将 JSX 编译成 React.Component 函数的调用,React.Component 函数执行后将生成虚拟DOM,然后将虚拟DOM渲染到浏览器上

在这里插入图片描述

<div id="root"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>

<!-- 不需要引用babel文件 -->
<!-- script 标签不需要type -->
<script>
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        msg: 'hello',
      };
    }

    render() {
      const { msg } = this.state;
      let btnClick = () => {
        console.log('btnClick');
      };

      const element = /*#__PURE__*/ React.createElement(
        'div',
        {
          className: 'active',
          id: 'box',
        },
        /*#__PURE__*/ React.createElement(
          'span',
          {
            'data-id': '1',
            onClick: btnClick,
          },
          /*#__PURE__*/ React.createElement(
            'label',
            null,
            '\u5B50\u5143\u7D201'
          )
        ),
        /*#__PURE__*/ React.createElement(
          'span',
          {
            'data-id': '2',
          },
          msg
        )
      );

      // 虚拟DOM树
      console.log(element);

      return element;
    }
  }

  const root = ReactDOM.createRoot(document.getElementById('root'));
  // 注意这里的写法
  root.render(React.createElement(App, null));
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值