React 之 基础与核心语法

一、React基础-初体验

1. react

React :用于构建用户界面的 JavaScript 库

2. react特点

声明式

声明式编程:
  • 声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI

  • 它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面

组件化

组件化开发:将复杂的界面拆分成一个个小的组件

多平台适配

  • 开发Web页面

  • ReactNative,用于开发移动端跨平台

  • ReactVR,用于开发虚拟现实Web应用程序

3. React的开发依赖概念

开发React必须依赖三个库:
  • react:包含react所必须的核心代码

  • react-dom:react渲染在不同平台所需要的核心代码

  • babel:将jsx转换成React代码的工具

react

react包中包含了react web和react-native所共同拥有的核心代码

react-dom

在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里
原因就是react-native
  • react-dom针对web和native所完成的事情不同

  • web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中

  • native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)

babel

babel : 是目前前端使用非常广泛的编译器、转移器。
  • Babel,又名 Babel.js

  • 当下很多浏览器并不支持ES6的语法,通过Babel工具,将ES6转成大多数浏览器都支持的ES5语法

  • 在react中,直接编写jsx(JavaScript XML)的语法,并且让babe转换成React.createElement

  • 可不使用babel,但是要用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差

4. React的开发依赖引入

方式一:通过npm管理

ps : 后续脚手架中使用

方式二:CDN引入

crossorigin属性 :目的是为了拿到跨域脚本的错误信息
ps : 也可以下载依赖包到本地,然后本地引入
  • react

  • react-dom

  • babel

5. 渲染概念理解

script标签

script : 添加 type="text/babel",作用是可以让babel解析jsx的语法

ReactDOM. createRoot

ReactDOM.createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个根中

root.render

root.render函数 : 要渲染的根组件

6. 案例一 - hello world

01 - 初体验

<!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>Document</title>
    <!-- 导入三个依赖 -->
    <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>
  </head>
  <body>
    <div id="root"></div>
    <div id="app"></div>
    <script type="text/babel">
      // react 16的写法
      // ReactDOM.render(<h2>123123</h2>, document.querySelector('#root'));

      // react 16
      const root = ReactDOM.createRoot(document.querySelector('#root'));
      // 创建两个跟
      const app = ReactDOM.createRoot(document.querySelector('#app'));
      // 渲染
      root.render(<h2>hello world</h2>);
      app.render(<h2>hello react</h2>);
    </script>
  </body>
</html>

02 - 初步优化 : 变量和事件

<!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>Document</title>
    <!-- 导入三个依赖 -->
    <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>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const root = ReactDOM.createRoot(document.querySelector('#root'));

      // 2. 定义变量
      let message = 'world';

      // 4. 监听按钮点击
      const btnClick = () => {
        // 5. 修改数据
        message = message === 'react' ? 'world' : 'react';
        // 6. 重新渲染一次,不会自动重新执行render,需要手动
        rootRender();
      };

      // 1. 默认调用一次
      rootRender();

      // 封装渲染流程
      function rootRender() {
        root.render(
          <div>
            <h2>3. 变量 : hellow {message}</h2>
            <button onClick={btnClick}>修改文本</button>
          </div>
        );
      }
    </script>
  </body>
</html>

03 - 再次优化:组件化开发重构

暂时使用类的方法封装组件
  • 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素)

  • 继承自React.Component

  • 实现当前组件的render函数

  • render当中返回的jsx内容,就是之后React渲染的内容

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    // 渲染内容 render 方法
    render() {
      return (
        <div>
          <h2>hello world</h2>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

组件化 - 数据依赖

在组件中的数据,可以分成两类:
  • 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容

  • 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容

参与界面更新的数据也可以称之为是参与数据流,这个数据是定义在当前对象的state中
  • 可以通过在构造函数中 this.state = {定义的数据}

  • 数据发生变化时,可以调用 this.setState 来更新数据,并且通知React进行update操作

  • 在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    // 1. 组件数据
    constructor() {
      super();
      // 动态的数据,需要放在这里!
      this.state = {
        message: 'hello world'
      };
    }
    // 2. 渲染内容 render 方法
    render() {
      return (
        <div>
          <h2>{this.state.message}</h2>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

组件化 – 事件绑定

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

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

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    // 1. 组件数据
    constructor() {
      super();
      // 动态的数据,需要放在这里!
      this.state = {
        message: 'hello world'
      };
    }
    // 2. 组件方法( 实例方法 )
    btnClick() {
      /**
       * this为undefined => this.btnClick
       * 需要绑定this过来 => this.btnClick.bind(this)
       */
      // 修改数据 this.setState为继承类里面的方法
      /**
       * 内部完成了两件事情
       * 1. 将state中的message的值修改掉
       * 2. 自动重新执行render函数
      */
      this.setState({
        message: 'hello react'
      });
    }

    // 3. 渲染内容 render 方法
    render() {
      console.log(this); // 这里有当前的实例
      return (
        <div>
          <h2>{this.state.message}</h2>
          <button onClick={this.btnClick.bind(this)}>修改文本</button>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

7. 案列二 - 列表展示

<body>
  <div id="root"></div>
  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          list: ['星际穿越', '火影大战猪猪侠', '海绵宝宝泡泡堂', '爱情公寓']
        };
      }
      render() {
        return (
          // 循环
          <ul>
            {this.state.list.map((item) => {
              return <li key={item}>{item}</li>;
            })}
          </ul>
        );
      }
    }

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

8. 案例三 - 计数器案例

<body>
  <div id="root"></div>
  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          count: 0
        };
      }
      increment = () => {
        this.setState({
          count: this.state.count + 1
        });
      };
      decrement = () => {
        this.setState({
          count: this.state.count - 1
        });
      };
      render() {
        const { count } = this.state;

        return (
          <div>
            <h1>当前计数 : {count}</h1>
            <button onClick={this.increment}>+1</button>
            <button onClick={this.decrement}>-1</button>
          </div>
        );
      }
    }

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

9. 生成代码片段

"react-html-demo": {
  "prefix": "react-html",
  "body": [
    "<!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>Document</title>",
    "    <script src=\"./lib/react.js\"></script>",
    "    <script src=\"./lib/react-dom.js\"></script>",
    "    <script src=\"./lib/babel.js\"></script>",
    "  </head>",
    "  <body>",
    "    <div id=\"root\"></div>",
    "    <script type=\"text/babel\">",
    "      /**",
    "       * 创建App组件",
    "       */",
    "      class App extends React.Component {",
    "        constructor() {",
    "          super();",
    "          this.state = {",
    "            counter: 10",
    "          };",
    "        }",
    "        render() {",
    "          const { counter } = this.state;",
    "",
    "          return (",
    "            <div>",
    "              <h1>当前计数 : {counter}</h1>",
    "            </div>",
    "          );",
    "        }",
    "      }",
    "",
    "      /**",
    "       * 创建根组件并渲染",
    "       */",
    "      const root = ReactDOM.createRoot(document.querySelector('#root'));",
    "      root.render(<App />);",
    "    </script>",
    "  </body>",
    "</html>",
    ""
  ],
  "description": "react-html-demo"
}

二、React基础 - JSX语法

1. 概念

JSX : React - JSX
  • JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML

  • 因为看起就是一段XML语法

  • 用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用 => html in js

  • 不同于Vue中的模块语法,不需要学习模块语法中的一些指令

  • 比如v-for、v-if、v-else、v-bind

2. 为什么React选择了JSX

React认为渲染逻辑本质上与其他UI逻辑存在内在耦合 => html、css、js分不开,干脆写到一起
  • 比如UI需要绑定事件(button、a原生等等)

  • 比如UI中需要展示数据状态

  • 比如在某些状态发生改变时,又需要改变UI

  • 他们之间是密不可分,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起

  • 这个地方就是组件(Component)

3. JSX书写规范

  • 1. JSX的顶层只能有一个根元素

  • 所以我们很多时候会在外层包裹一个div元素(或者使用后面我们学习的Fragment)

  • 2. 为了方便阅读,通常在jsx的外层包裹一个小括号()

  • 这样可以方便阅读,并且jsx可以进行换行书写

  • 3. JSX中的标签可以是单标签,也可以是双标签

  • 如果是单标签,必须以/>结尾

  • 4. JSX中注释的写法 => [ .jsx文件中 -- ctrl + / ] 即可

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

  return (
    <div>
      {
        // 注释的写法1
      }
      {/* 注释的写法2 */}
      <h1>当前计数 : {counter}</h1>
    </div>
  );
}

4. React变量使用

JSX嵌入变量作为子元素

情况一 : 直接显示

当变量是Number、String、Array类型时,可以直接显示
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      // Number/String/Array => 直接显示
      num: 10,
      str: 'abc',
      arr: [1, 2, 3],
    };
  }
  render() {
    // 直接显示
    const { num, str, arr } = this.state;

    return (
      <div>
        {/* Number/String/Array => 直接显示,其中Array会依次拿出元素遍历出来 */}
        <h1>{num}</h1>
        <h1>{str}</h1>
        <h1>{arr}</h1>
      </div>
    );
  }
}

情况二 : 不显示

当变量是null、undefined、Boolean类型时,内容为空
ps : 如果希望可以显示null、undefined、Boolean,那么需要转成字符串
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      // null/undefined/boolean => 页面不显示
      aa: null,
      bb: undefined,
      cc: true,
      dd: false
    };
  }
  render() {
    // 页面不显示
    const { aa, bb, cc, dd } = this.state;

    return (
      <div>
        {/* null/undefined/boolean => 界面上不显示,也不会报错*/}
        <h2>{aa}</h2>
        <h2>{bb}</h2>
        <h2>{cc}</h2>
        <h2>{dd}</h2>
        {/* 可以转成字符串显示出来 */}
        <h2>{aa + ''}</h2>
        <h2>{String(bb)}</h2>
        <h2>{cc.toString()}</h2>
      </div>
    );
  }
}

情况三: Object对象类型不能作为子元素

Object对象类型不能作为子元素(not valid as a React child)
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      // Object类型,不能作为子元素显示,报错
      obj: {
        name: 'coder',
        age: 18
      }
    };
  }
  render() {
    // Object类型,不能作为子元素显示,报错
    const { obj } = this.state;

    return (
      <div>
        {/* Object类型,不能作为子元素显示,报错 */}
        {/* <h3>{obj}</h3> */}

        {/* Object中的属性可以 */}
        <h3>{obj.name}</h3>
        <h3>{obj.age}</h3>
      </div>
    );
  }
}

JSX嵌入表达式

运算表达式

render() {
  const firstName = 'coder';
  const lastName = 'star';
  return (
    <div>
      <h1>{1 + 2}</h1>
      <h1>{firstName + lastName}</h1>
    </div>
  );
}

三元运算符

render() {
  const boo = true;
  const age = 18;
  return (
    <div>
      <h1>{age >= 18 ? '成年' : '未成年'}</h1>
      <h1>{boo ? 'true' : 'false'}</h1>
    </div>
  );
}

执行函数

// 抽离出来,在函数中做复杂的操作
getName = () => {
  return 'abc' + 'sdfs' + 'we';
};

render() {
  return (
    <div>
      <h1>{this.getName()}</h1>
    </div>
  );
}

JSX绑定属性

render() {
  const title = 'title';
  const imgUrl = 'https://n.sinaimg.cn/sinakd10111/533/w533h800/20200713/6340-iwhseit8630287.jpg';

  return (
    <div>
      <h2 title={title}></h2>
      <img src={imgUrl} />
    </div>
  );
}

JSX绑定类Class

绑定class属性 : 最好使用className

写法一 : 字符串拼接

render() {
  const isActive = false;

  return (
    <div>
      <h2 className='a b'>111</h2>
      {/* 动态绑定class */}
      <h2 className={`a b c ${isActive ? 'active' : ''}`}>222</h2>
    </div>
  );
}

写法二 : 放入数组中

render() {
  const isActive = true;
  const classList = ['a', 'b'];
  isActive && classList.push('active');

  return (
    <div>
      <h2 className='a b'>111</h2>
      {/* 动态绑定class */}
      <h2 className={classList.join(' ')}>222</h2>
    </div>
  );
}

写法三 : 插件 - classnames

脚手架中使用该插件 => npm insatall classnames

JSX绑定样式Style

render() {
  const style = { color: 'blue', fontSize: '26px' };
  return (
    <div>
      {/* 1. 不支持 */}
      {/* <h2 style='color:red'>111</h2>*/}

      {/* 2. { {中间是对象} } */}
      <h2 style={
    
    { color: 'red', fontSize: '26px' }}>111</h2>

      {/* 3. { {中间是对象} } */}
      <h2 style={style}>222</h2>
    </div>
  );
}

5. React事件绑定

this的绑定问题

事件绑定中的this默认情况下是undefined
  • 很奇怪,居然是undefined

  • 因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象 -比如说是button对象

  • 这次因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象

  • 那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined

  • onClick = {this.fn} => 这个时候函数还没有执行,调用的时候可能是默认调用

this问题
因其内部很可能是默认调用,如果不传递this进去,在严格模式下,方法内部的this就为undefined,所以通过bind绑this进去,不管后面怎么调用,this都为绑定的this

写法一 : 传入函数时绑定this

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    // 1. 组件数据
    constructor() {
      super();
      // 动态的数据,需要放在这里!
      this.state = {
        message: 'hello world'
      };
    }
    // 2. 组件方法( 实例方法 )
    btnClick() {
      /**
       * this为undefined => this.btnClick
       * 需要绑定this过来 => this.btnClick.bind(this)
       */
      // 修改数据 this.setState为继承类里面的方法
      /**
       * 内部完成了两件事情
       * 1. 将state中的message的值修改掉
       * 2. 自动重新执行render函数
      */
      this.setState({
        message: 'hello react'
      });
    }

    // 3. 渲染内容 render 方法
    render() {
      console.log(this); // 这里有当前的实例
      return (
        <div>
          <h2>{this.state.message}</h2>
          <button onClick={this.btnClick.bind(this)}>修改文本</button>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

写法二 : constructor中绑定好this

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        message: 'hello world'
      };
      /**
       * 这里绑定好
      */
      // 为了在回调中使用 `this`,这个绑定是必不可少的
      this.btnClick = this.btnClick.bind(this);
    }
    btnClick() {
      this.setState({
        message: 'hello react'
      });
    }

    render() {
      // 这里就不用再次绑定了
      return (
        <div>
          <h2>{this.state.message}</h2>
          <button onClick={this.btnClick}>修改文本</button>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  // APP 根组件
  root.render(<App />);
</script>

写法三 : 函数使用箭头函数

使用箭头函数 : 箭头函数中没有this,自动去上层作用域中找到this
<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        message: 'hello world'
      };
    }
    /**
     * 使用箭头函数 : 也没有问题!
    */
    btnClick = () => {
      this.setState({
        message: 'hello react'
      });
    };

    render() {
      return (
        <div>
          <h2>{this.state.message}</h2>
          <button onClick={this.btnClick}>修改文本</button>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

写法四 : 直接传入箭头函数 ( 推荐 )

<script type="text/babel">
  // 封装类组件
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        message: 'hello world'
      };
    }
    btnClick() {
      console.log(this);
      this.setState({
        message: 'hello react'
      });
    }

    render() {
      return (
        <div>
          <h2>{this.state.message}</h2>
          {
            /**
            * 直接使用箭头函数
            * 当点击时候,执行箭头函数,然后执行btnClick,这里就是隐式绑定了
            */
          }
          <button onClick={() => this.btnClick()}>修改文本</button>
        </div>
      );
    }
  }

  // 将组件渲染到界面上
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

事件参数传递

event参数

class App extends React.Component {
  constructor() {
    super();
  }
  btnClick(e) {
    console.log('btnClick', this, e);
  }
  render() {
    return (
      <div>
        {/* 方式一 : 有事件参数,没有this */}
        <button onClick={this.btnClick}>按钮</button>

        {/* 方式二 : 有事件参数,有this */}
        <button onClick={this.btnClick.bind(this)}>按钮</button>
        
        {/* 方式三 : 须手动传递下事件参数,有this */}
        <button onClick={(e) => this.btnClick(e)}>按钮</button>
      </div>
    );
  }
}

传递额外参数

class App extends React.Component {
  constructor() {
    super();
  }
  /**
   * 记住顺序,event是在最后
   *    bind的时候把前两个参数传递过来了
   *    调用的时候,event才传过来
   */
  btnClick(name, age, event) {
    console.log('btnClick', name, age, event);
  }
  render() {
    return (
      <div>
        {/* 方式一 : 传递参数需要记住顺序 */}
        <button onClick={this.btnClick.bind(this, 'coder', 18)}>按钮</button>

        {/* 方式二 : 传递参数时,很清晰 */}
        <button onClick={(e) => this.btnClick('star', 17, e)}>按钮</button>
      </div>
    );
  }
}

案例

<!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>Document</title>
    <script src="./lib/react.js"></script>
    <script src="./lib/react-dom.js"></script>
    <script src="./lib/babel.js"></script>
    <style>
      .active {
        color: blue;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      /**
       * 创建App组件
       */
      class App extends React.Component {
        constructor() {
          super();
          this.state = {
            movies: ['海绵宝宝', '派大星', '章鱼哥', '蟹老板'],
            currentIndex: 0
          };
        }

        itemClick(index) {
          this.setState({
            currentIndex: index
          })
        }
        render() {
          const { movies, currentIndex } = this.state;

          return (
            <div>
              <ul>
                {movies.map((item, index) => {
                  return (
                    <li 
                      className={currentIndex === index ? 'active' : ''}
                      key={item}
                      onClick={() => this.itemClick(index)}
                    >
                      {item}
                    </li>
                  );
                })}
              </ul>
            </div>
          );
        }
      }

      /**
       * 创建根组件并渲染
       */
      const root = ReactDOM.createRoot(document.querySelector('#root'));
      root.render(<App />);
    </script>
  </body>
</html>

6. React条件渲染

方式一 : 使用if判断

适合 逻辑较多 的情况

方式二 : 三元运算符

适合 逻辑比较简单 的情况

方式三:&& - 与运算符

适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染

方式四:v-show的效果

案例

<script type="text/babel">
  /**
   * 创建App组件
   */
  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        isShow: true
      };
    }

    tabClick() {
      this.setState({
        isShow: !this.state.isShow
      });
    }

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

      return (
        <div>
          <button onClick={() => this.tabClick()}>切换</button>

          {isShow && <h2>我显示了</h2>}
          
          <h2 style={
    
    { display: isShow ? 'block' : 'none' }}>我也来!</h2>
        </div>
      );
    }
  }

  /**
   * 创建根组件并渲染
   */
  const root = ReactDOM.createRoot(document.querySelector('#root'));
  root.render(<App />);
</script>

7. React列表渲染

render - 写法一

使用for循环
render() {
  // 1. 创建元素数组
  const elList = [];
  for (let i = 0; i < this.state.list.length; i++) {
    const movies = this.state.list[i];
    // 2. 创建li元素
    const el = <li key={i}>{movies}</li>;
    // 3. 推入元素数组中
    elList.push(el);
  }
  return (
    // 4. 直接放入数组
    <ul>{elList}</ul>
  );
}

render - 写法二

使用map循环 : 展示列表最多的方式就是使用数组的map高阶函数
render() {
  return (
    // 循环
    <ul>
      {this.state.list.map((item) => {
        return <li key={item}>{item}</li>;
      })}
    </ul>
  );
}

三、JSX的本质和原理

1. 概念

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

createElement需要传递三个参数:

  • 参数一:type

  • 当前ReactElement的类型

  • 如果是标签元素,那么就使用字符串表示 “div”

  • 如果是组件元素,那么就直接使用组件的名称

  • 参数二:config

  • 所有jsx中的属性都在config中以对象的属性和值的形式存储

  • 比如传入className作为元素的class

  • 参数三:children

  • 存放在标签中的内容,以children数组的方式进行存储

  • 如果是多个元素,React内部有对它们进行处理

2. createElement源码

3. 在Babel官网查看

4. 直接编写jsx代码

<!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>Document</title>
    <script src="./lib/react.js"></script>
    <script src="./lib/react-dom.js"></script>
    <!-- 不使用babel -->
    <!-- <script src="./lib/babel.js"></script> -->
  </head>
  <body>
    <div id="root"></div>
    <!-- 不使用babel -->
    <!-- <script type="text/babel"> -->
    <script>
      /**
       * 创建App组件
       */
      class App extends React.Component {
        constructor() {
          super();
          this.state = {
            counter: 10
          };
        }
        render() {
          const { counter } = this.state;

          const ele = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
            className: "header"
          }, "header"), /*#__PURE__*/React.createElement("div", {
            className: "main"
          }, /*#__PURE__*/React.createElement("ul", {
            className: "nav"
          }, /*#__PURE__*/React.createElement("li", {
            className: "item"
          }, "1"), /*#__PURE__*/React.createElement("li", {
            className: "item"
          }, "2"), /*#__PURE__*/React.createElement("li", {
            className: "item"
          }, "3"), /*#__PURE__*/React.createElement("li", {
            className: "item"
          }, "4"), /*#__PURE__*/React.createElement("li", {
            className: "item"
          }, "5"))), /*#__PURE__*/React.createElement("div", {
            className: "footer"
          }, "footer"));

          return ele
        }
      }

      /**
       * 创建根组件并渲染
       */
      const root = ReactDOM.createRoot(document.querySelector('#root'));
      // 
      root.render(React.createElement(App, null));
    </script>
  </body>
</html>

5. 虚拟DOM

01 - 虚拟DOM创建过程

  • 通过 React.createElement 最终创建出来一个 ReactElement对象

  • React利用ReactElement对象组成了一个JavaScript的对象树

  • JavaScript的对象树就是虚拟DOM(Virtual DOM)

02 - 虚拟DOM的作用

  • 1. 在更新数据时不会把整一段全部重新渲染

  • 会在新旧虚拟dom之间进行diff算法计算,然后再重新渲染有差别的部分

  • 2. 跨平台,使用React-native原生开发

  • 虚拟DOM就是js对象,ReactElement对象

  • 根据环境不同,桥接到环境时,生成不同的东西

  • web端 => div、button IOS、Android端 => 原生控件、view、...

  • 3. 从命令式编程转到了声明式编程的模式

6. jsx – 虚拟DOM – 真实DOM

7. 声明式编程

虚拟DOM帮助我们从命令式编程转到了声明式编程的模式

React官方的说法:

  • Virtual DOM 是一种编程理念

  • 在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象

  • 可以通过root.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调

这种编程的方式赋予了React声明式的API :

  • 只需要告诉React希望让UI是什么状态

  • React来确保DOM和这些状态是匹配的

  • 不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来

四、React脚手架

1. 概念

01 - 脚手架

脚手架 : 让项目从搭建到开发,再到部署,整个流程变得快速和便捷

现在比较流行的三大框架都有属于自己的脚手架:

  • Vue的脚手架:@vue/cli

  • Angular的脚手架:@angular/cli

  • React的脚手架:create-react-app

02 - 安装node

网址 : Node.js

2. React项目

01 - 安装脚手架

npm install create-react-app -g

// 查看安装的版本
create-react-app --version

02 - 创建项目

  • 项目名称不能包含大写字母

  • create-react-app 项目名称

03 - 运行项目

npm run start

04 - 目录结构分析

3. webpack配置

如果希望看到webpack的配置信息,
可以执行命令 =>package.json文件中的一个脚本:"eject": "react-scripts eject"
这个操作是不可逆的

4. 删除项目结构

  • 将src下的所有文件都删除

  • 将public文件下出列favicon.ico和index.html之外的文件都删除掉

5. 开始编写代码

01 - 在src下创建 index.js

/**
 * 入口文件
 */
// react18 从这里引入ReactDOM
import ReactDOM from 'react-dom/client';

import App from './App.jsx';

const root = ReactDOM.createRoot(document.querySelector('#root'));

/**
 * <App /> => 这里实际上就是App类的实例,相当于创建了一个实例
 */
root.render(<App />);

02 - 在src下创建 App.jsx

/**
 * 内部有单独导出Component
 * import React from 'react';
 * import { Component } from 'react';
 * 所以可以合并
 */

import React, { Component } from 'react';

class App extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  render() {
    return (
      <div>
        <h2>title</h2>
      </div>
    );
  }
}

// 导出类
export default App

猜你喜欢

转载自blog.csdn.net/a15297701931/article/details/129732320