React学习笔记[1]✨~第一个React程序👻

998 阅读4分钟

我正在参加「掘金·启航计划」

React开发依赖

react:包含react所必须的核心代码

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

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

三个库各司其职,在React0.14之前是没有react-dom这个概念的,所以所有核心代码都包含在react中

目前之所以拆分成react与react-dom是由于react native:

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

react dom针对web和native所完成的事情不同

  • web端:react dom会将jsx最终渲染成真实的dom,显示在浏览器中
  • native端:react dom会将jsx最终渲染成原生的控件(如Android的Button、IOS的UI Button)

React与Babel的关系

Babel是目前前端使用非常广泛的编译器

  • 比如很多浏览器不支持ES6语法,我们在编写源码时使用ES6编写后可以通过Babel将ES6转换成大多浏览器支持的ES5语法

默认情况下开发React项目时可以不使用Babel,但是如果使用React的createElement来编写源码非常复杂而且可读性很差,所以我们可以直接通过编写JSX语法,并让Babel转换成React.createElement

第一个React程序

在开发前,需要引入react、react-dom、babel这三个依赖,这里暂时采用CDN引入

除了可以采用CDN引入外,还可以下载到本地进行引入,或通过npm安装依赖

<!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>Hello React</title>
</head>
<body>

  <div id="root"></div>
  <div id="app"></div>
  <!-- CDN引入 -->
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> 
  <!-- babel -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </script>
</body>
</html>

引入依赖后便可以在script中编写React代码了

  • 这里需要注意的是,由于我们想编写JSX语法,然后通过Babel去编译,所以需要在script标签上添加"type='text/babel'"来告知babel需要进行编译
<!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>
</head>

<body>
  <div id="root"></div>
  <div id="app"></div>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <!-- babel -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    const root = ReactDOM.createRoot(document.querySelector('#root'))
    root.render(<h2>Hello World</h2>)

    const app = ReactDOM.createRoot(document.querySelector('#app'))
    app.render(<h2>Hello React</h2>)
  </script>
</body>

</html>

在React18之后,是可以创建多个React根的:

通过ReactDOM.createRoot(dom)来创建React根,之后渲染的内容会包含在这个根中,然后再通过创建的React根去调用render函数(传入要渲染的组件)去渲染

在React18之前是通过ReactDOM.render(根组件,根节点)来渲染页面:

ReactDOM.render(<h2>Hello World</h2>, document.querySelector("#root"))

案例一:点击按钮修改文本案例

实现方式一

如果我们想要实现点击按钮来修改页面中展示的文本,也就是点击按钮后让页面重新渲染,那么可以分以下几个步骤

  • 将文本内容放到变量中保存
  • 监听按钮点击时间,点击时修改文本
  • 修改文本后重新执行root.render方法,让页面重新渲染

按照以上的思路,可以将root.render方法提取出来,然后在页面初次渲染与点击按钮修改文本后进行调用即可

<!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>
</head>

<body>
  <div id="root"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    const root = ReactDOM.createRoot(document.querySelector('#root'))

    let message = 'Hello World'
    function handleClick() {
      message = 'Hello React'
      // 重新render
      renderFn()
    }

    function renderFn() {
      root.render((
        <div>
          <h2>{message}</h2>
          <button onClick={handleClick}>点击修改</button>
        </div>
      ))
    }
    renderFn()
  </script>
</body>

</html>
  • 在JSX中,使用'{}'来包裹变量
  • 点击事件的通过onClick={事件名}来绑定
  • 如果render中包裹的JSX是嵌套的HTML结构时,我们可以使用'()'来包裹表示这是一个整体

实现方式二:使用组件化开发优化案例

root.render中的参数可以是一个HTML元素,也可以是一个组件

所以可以将上面的业务逻辑封装到一个组件中,然后传入到ReactDOM.render函数中

在React中可以以的方式来封装组件:

  1. 首先定义一个类去继承React.Component
  • 类名必须要大写,小写会被认为是HTML元素
  1. 定义数据
  • 定义的数据可以分为两类:参与页面更新的数据(也就是当数据改变时需要重新渲染页面)、以及不参与界面更新的数据
  • 参与界面更新的数据需要定义在当前对象的state中,可以在构造函数中通过this.state = {xxx}的方式来定义
  • 当数据发生变化时,可以通过调用this.setState来更新数据,此时React会执行update操作重新调用render函数来更新界面
  1. 实现当前组件的render函数
  • 在render函数中,return的JSX就是React会帮助我们渲染的内容
<!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>
</head>

<body>
  <div id="app"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          message: 'Hello World'
        }
        this.btnClick = this.btnClick.bind(this)
      }
      btnClick() {
        this.setState({
          message: 'Hello React'
        })
      }
      render() {
        return (
          <div>
            <h1>{this.state.message}</h1>
            <button onClick={this.btnClick}>修改文本</button>
          </div>
        )
      }
    }

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

需要注意的是,绑定的事件的this默认情况下是undefined,需要我们手动绑定一下this

一般会在constructor中统一对事件进行this的绑定,也可以在JSX中绑定,比如:onClick={this.btnClick.bind(this)}

之所以默认情况下this是undefined是由于在class中是开启了严格模式的,而且在我们进行事件绑定时React内部再执行函数时并没有绑定this,所以会是undefined

因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如这里的button对象)

而React之所以没绑定this是因为React并不是直接渲染成真实DOM的,我们在JSX中编写的button其实只是一个语法糖,其本质是React的Element对象

所以在这里发生监听的时候,并没有绑定this,在严格模式下没有绑定this默认是undefined,所以需要我们手动绑定一下,不然在执行btnClick的时候this.setState相当于undefined.setState,会报如下错误:

案例二:展示电影列表

<!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>
</head>

<body>
  <div id="app"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          movies: ['哪吒', '独行月球', '杨戬']
        }
      }
      btnClick() {
        this.setState({
          message: 'Hello React'
        })
      }
      render() {
        return (
          <ul>
            {this.state.movies.map(movie => <li>{movie}</li>)}
          </ul>
        )
      }
    }

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

</html>

由于JSX中是可以直接展示数组的,所以可以将movies数组直接map成li数组进行展示即可

案例三:计数器

<!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>
</head>

<body>
  <div id="app"></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super()
        this.state = {
          count: 100
        }
        this.increment = this.increment.bind(this)
        this.decrement = this.decrement.bind(this)
      }
      increment() {
        this.setState({ count: this.state.count + 1 })
      }
      decrement() {
        this.setState({ count: this.state.count - 1 })
      }
      render() {
        return (
          <div>
            <h1>{this.state.count}</h1>
            <button onClick={this.increment}>+1</button>
            <button onClick={this.decrement}>-1</button>
          </div>
        )
      }
    }

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

</html>