一、React初体验

145 阅读5分钟

一、React的介绍和特点

React的介绍(技术角度)

React的特点 - 声明式编程

  • 声明式编程
    • 声明式编程是目前整个大前端开发的模式:Vue、React、Flutter、SwiftUI
    • 只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染UI界面

声明式编程.png

React特点 - 组件化开发

  • 组件化开发
    • 组件化开发页面目前前端的流行趋势,将复杂的界面拆分成一个个小小的组件
    • 合理的进行组件的划分和设计

组件化开发.png

React的特点 - 多平台适配

  • 多平台适配
    • 2013年,React发布之初主要是开发Web页面
    • 2015年,Facebook推出了ReactNative,用于开发移动端跨平台;(目前Flutter非常火爆,但仍有很多公司在使用ReactNative)
    • 2017年,FaceBook推出ReactVR,用于开发虚拟现实Web应用程序;(VR也会是一个火爆的应用场景)

二、Hello React案例

Hello React案例说明

  • 需求
    • 在界面显示文本:Hello World
    • 点击下方的按钮,文本改变为 Hello React

Hello World

  • 第一步:在界面上通过React显示一个Hello World
    • 注意:编写React的script代码中,必须添加 type="text/babel",作用是可以让babel解析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>

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

        // 1.将文本定义为变量
        let msg = 'Hello World'
        // 2.监听按钮的点击
        function btnClick() {
            // 2.1 修改文本
            msg = 'Hello React'

            // 2.2 重新渲染界面
            rootRender()
        }

        // 3.渲染函数
        function rootRender() {
            root.render((
                <div>
                    <h2>{msg}</h2>    
                    <button onClick={btnClick}>修改文本</button>
                </div>
            ))
        }

        rootRender()
    </script>
  • ReactDOM.createRoot 函数:用于创建一个React根,之后渲染的内容会包含在这个根中
    • 参数:将渲染的内容,挂载到那个HTML元素上
      • 定义一个 id 为 root 的 div
  • root.render 函数
    • 参数:要渲染的根组件
  • 可以通过 {} 语法来引入外部的变量或者表达式

三、React开发依赖分析

React的开发依赖

  • 开发React必须依赖三个库

    • react:包含react所必须的核心代码
    • react-dom:react渲染在不同平台所需要的核心代码
    • babel:将jsx转换成React代码的工具
  • react依赖繁琐

    • 三个库各司其职,目的让每一个库只单纯做自己的事情
    • React0.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)

Babel 和 React 之间的关系

  • babel是什么

    • babel.js
    • 目前前端使用广泛的编译器、转移器
    • 很多浏览器不支持ES6语法,但我们希望在开发中使用它
    • 编写源码时可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5语法
  • React 和 Babel 关系

    • 默认情况下,开发React可以不使用Babel
    • 前提是得自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差
    • 可以直接编写jsx(JavaScript XML)的语法,并让babel转换成 React.createElement

React 的依赖引入

  • 添加依赖的方式

    • CDN引入
    • 下载后,添加本地依赖
    • 通过npm管理(脚手架使用)
  • 暂时使用 CDN引入

    • 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>

四、React组件化的封装

Hello React - 组件化开发

  • 封装成一个组件
    • root.render参数是一个HTML元素或者一个组件
    • 将之前的业务逻辑封装到一个组件中,然后传入到 ReactDOM.render 函数中的第一个参数
  • React中如何封装一个组件呢?类/函数
    • 定义一个类(类名大写,组件的名称必须大写,小写会被认为是HTML元素),继承自 React.Component
    • 实现当前组件的render函数
      • render 当中返回的jsx内容,就是之后React会帮助我们渲染的内容

组件化 - 数据依赖

  • 数据在哪里?
  • 组件中的数据分为两类
    • 参与界面更新的数据:当数据变化时,需要更新组件渲染的内容
    • 不参与界面更新的数据:当数据变化时,不需要更新将组件渲染的内容
  • 参与界面更新的数据称之为参与数据流,这个数据定义在当前对象的 state 中
    • 通过在构造函数中 this.state = {定义的数据}
    • 当数据发生变化时,可以调用 this.setState 来更新数据,并通知React进行update操作
      • 在进行update操作时,就会重新调用render函数,并且使用最新的数据渲染界面

五、React数据事件处理

组件化 - 事件绑定

  • 组件化问题二:事件绑定中的this
    • 在类中直接定义一个函数,将这个函数绑定到元素的 onClick 事件上,当前这个函数的 this 指向谁?
  • 默认情况下是 undefined
    • 在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(如button对象)
    • 因为React并不是直接渲染成真实的DOM,我们编写的button只是一个语法糖,本质是 React Element 对象
    • 在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个 undefined
  • 在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,必须拿到当前对象的 this
    • 需要在传入函数时,给这个函数直接绑定 this
    • 类似:<button onClick={this.btnClick.bind(this)}>改变文本</button>
    <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>

    <script type="text/babel">
        // 使用组件进行重构代码
        // 类组件和函数式编程
        class App extends React.Component {
            // 组件数据
            constructor() {
                super()
                this.state = {
                    msg: 'Hello World'
                }

                // 对需要绑定的方法,提前显式绑定好this
                this.btnClick = this.btnClick.bind(this)
            }

            // 组件方法(实例方法)
            btnClick = () => {
                // 内部完成了两件事情:1.将state中的msg值修改 2.自动重新执行render函数渲染界面
                this.setState({
                    msg: 'Hello React'
                })
            }

            // 渲染内容 render 方法
            render() {
                return (
                    <div>
                        <h2>{this.state.msg}</h2>    
                        <button onClick={this.btnClick}>修改文本</button>
                    </div>
                )
            }
        }

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

六、React其他案例实现

电影列表案例

    <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>

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

        // 封装App组件
        class App extends React.Component {
            constructor() {
                super()

                this.state = {
                    movies: ['流浪地球', '独行月球', '大话西游', '星际穿越']
                }
            }

            render() {
                return (
                    <div>
                        <h2>电影列表</h2>
                        <ul>
                            {this.state.movies.map(i => {
                                return (
                                    <li key={i}>{i}</li>
                                )
                            })}
                        </ul>
                    </div>
                )
            }
        }

        // 2.渲染组件
        // jsx -> React.createElement
        root.render(<App />)
    </script>

计数器案例

    <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>

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

        class App extends React.Component {
            constructor() {
                super()
                this.state = {
                    count: 10
                }

                this.increment = this.increment.bind(this)
                this.decrement = this.decrement.bind(this)
            }

            render() {
                const { count } = this.state
                return (
                    <div>
                        <h2>当前计数:{count}</h2>    
                        <button onClick={this.increment}>+1</button>
                        <button onClick={this.decrement}>1</button>
                    </div>
                )
            }

            // 组件方法
            increment() {
                this.setState({
                    count: this.state.count + 1
                })
            }

            decrement() {
                this.setState({
                    count: this.state.count - 1
                })
            }
        }

        root.render(<App />)
    </script>