Skip to content

一. React的介绍和特点


1.React的介绍(技术角度)

  • React是什么?
    • React:用于构建用户界面的 JavaScript 库
    • React的官网文档:https://zh-hans.reactjs.org/
image-20220901173629017

2.React的特点 – 声明式编程

  • 声明式编程:
    • 声明式编程是目前整个大前端开发的模式:VueReactFlutterSwiftUI
    • 它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面
image-20220901173715365

3.React特点 – 组件化开发

  • 组件化开发:
    • 组件化开发页面目前前端的流行趋势,我们会将复杂的界面拆分成一个个小的组件
    • 如何合理的进行组件的划分和设计也是后面我会讲到的一个重点
image-20220901174150026

4.React的特点 – 多平台适配

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

二. React开发依赖分析


1.React的开发依赖

  • 开发React必须依赖三个库:
    • react:包含react所必须的核心代码
    • react-domreact渲染在不同平台所需要的核心代码
    • babel:将jsx转换成React代码的工具
  • 第一次接触React会被它繁琐的依赖搞蒙,居然依赖这么多东西:
    • 对于Vue来说,我们只是依赖一个vue.js文件即可,但是react居然要依赖三个包
    • 其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情
    • React0.14版本之前是没有react-dom这个概念的,所有功能都包含在react
  • 为什么要进行拆分呢?原因就是react-native
    • react包中包含了react webreact-native所共同拥有的核心代码
    • react-dom针对webnative所完成的事情不同:
      • web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
      • native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的ButtoniOS中的UIButton

2.BabelReact的关系

  • babel是什么呢?
    • Babel,又名Babel.js
    • 是目前前端使用非常广泛的编译器、转移器
    • 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它
    • 那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法
  • ReactBabel的关系:
    • 默认情况下开发React其实可以不使用babel
    • 但是前提是我们自己使用React.createElement来编写源代码,它编写的代码非常的繁琐和可读性差
    • 那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们讲jsx代码转换成React.createElement
    • 后续还会详细讲到

3.React的依赖引入

  • 所以,我们在编写React代码时,这三个依赖都是必不可少的

  • 那么,如何添加这三个依赖:

    • 方式一:直接CDN引入
    • 方式二:下载后,添加本地依赖
    • 方式三:通过npm管理(后续脚手架再使用)
  • 暂时我们直接通过CDN引入,来演练下面的示例程序:

    • 这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息
html
  <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>

三. Hello React案例


  • 为了演练React,我们可以提出一个小的需求:
    • 在界面显示一个文本:Hello World
    • 点击下方的一个按钮,点击后文本改变为Hello React
image-20220901174614802
  • 当然,你也可以使用jQueryVue来实现,甚至是原生方式来实现,对它们分别进行对比学习

1.Hello World

  • 第一步:在界面上通过React显示一个Hello World
    • 注意:这里我们script标签中编写React代码,必须添加 type="text/babel",作用是可以让babel解析jsx的语法
image-20220901192219228
  • ReactDOM. createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个根中

    • 参数:将渲染的内容,挂载到哪一个HTML元素上
      • 这里我们已经提定义一个idapp的元素
  • root.render函数:

    • 参数:要渲染的根组件
  • 我们可以通过{}语法来引入外部的变量或者表达式

  • react18之前使用的是ReactDOM.render函数

    html
      <div id="root"></div>
      <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
      <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
      <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
      <script type="text/babel">
      	  //  js代码如下
      </script>
  • js

    jsx
    	const rootEl = document.querySelector('#root')
        ReactDOM.render(
           <h2>hello world</h2>, 
           rootEl
        )

2.Hello React – 错误做法

image-20220901200812713

3.Hello React – 正确做法

image-20220901200837195

四. React组件化的封装


1.Hello React – 组件化开发

  • 整个逻辑其实可以看做一个整体,那么我们就可以将其封装成一个组件:

    • root.render接收的参数可以是一个html元素或一个组件
    • 所以我们可以先将之前的业务逻辑封装到一个组件中,然后传入到ReactDOM.render 函数中的第一个参数
  • React中,如何封装一个组件呢?这里我们暂时使用类的方式封装组件:

    • 1.定义一个类,继承自React.Component
    • 2.实现当前类组件的render函数
      • render函数返回的jsx内容,就是之后React会帮助我们渲染的内容
  • 注意: 组件名称必须以大写字母开头

    • React 会将以小写字母开头的组件视为原生DOM 标签。例如,<div /> 代表 HTMLdiv标签,而 <Welcome /> 则代表一个组件,并且需在作用域内使用 Welcome
    jsx
    	class App extends React.Component {
            render() {
                return <h2>hello world</h2>
            }
        }
    
    	const root = ReactDOM.createRoot(document.querySelector('#root'))
        root.render(<App/>)

2.组件化 - 数据依赖

  • 组件化问题一:数据在哪里定义?

  • 在组件中的数据,我们可以分成两类:

    • 参与界面更新的数据:当数据变化时,需要更新组件渲染的内容
    • 不参与界面更新的数据:当数据变化时,不需要更新将组建渲染的内容
  • 参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state

    • 我们可以通过在构造函数中this.state = { 定义的数据 }
    • 当我们的数据发生变化时,我们可以调用this.setState 来更新数据,并且通知React进行update操作
      • 在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面
  • 注意:

    • this.setState方法是继承自React.Component的实例方法
    • 调用this.setState()会对当前this对象中的state中值进行一个修改,并且重新执行render函数
      • 重新执行render函数,会对有变化的数据重新生成一个虚拟DOM,然后通过diff算法来更新真实DOM
      • React内部没有对state数据做一个劫持,所以数据是没有响应式的,需要通过this.state来更新状态(类似于小程序的this.setData
      • Vue中是有对data中的数据做一个劫持,所以当数据发生改变时,会自动执行render函数
    react
        component.forceUpdate(callback)
  • 默认情况下,当组件的stateprops发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染

五. React数据事件处理


  • 组件化问题二:事件绑定中的this

    • 在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向的是谁呢?
  • 默认情况下是undefined

    • 很奇怪,居然是undefined
    • 因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象
    • 这是因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质是ReactElement对象
    • 那么在这里发生监听的时候,React在执行函数时并没有绑定this,默认情况下就是一个undefined
  • 为什么默认是undefined呢?

    • React内部就是将该引用传递给一个对象中,如下:

      jsx
      class App extends React.Component {
        changeText() {
          console.log(this)
        }
      
        render() {
          return (
            <div>
              <h2>{ this.state.message }</h2>
              <button onClick={ this.changeText }>改变文本</button>
            </div>
          )
        }
      }
      //  1. React内部对render方法返回的元素中所绑定的事件,并没有做一个对应的this绑定,而是将函数的引用传递了过去
      const btnEvents = { 
        onClick: this.changeText
      }
      React.creatElement('button', btnEvents)
      
      // 2. 然后当该button触发事件时,默认调用对应的回调函数
      const click = btnEvents.onClick
      click()
    • 而默认调用的函数中的this在普通环境下是window,严格环境是undefined,通过babel转化后的代码都是在严格环境下的,所以就导致了默认是undefined这种情况的出现

  • 我们在绑定的函数中,可能想要使用当前对象,比如执行this.setState 函数,就必须拿到当前对象的this

    • 我们就需要在传入函数时,给这个函数直接绑定this
    • 类似于如下写法:
    jsx
    class App extends React.Component {
      constructor() {
        super()
        this.changeText = this.changeText.bind(this) // 写法二
      }
    
      changeText() {
        console.log(this)
      }
    
      // 写法三:  class 的 field,ES13中,新增了定义class类中成员字段field的其他方式
      changeText = () => {
        console.log(this)
      }
    
      render() {
        return (
          <div>
            {/*  写法一 */}
            <button onClick={ this.changeText.bind(this) }>改变文本</button>
            {/*  写法四 */}
            <button onClick={ () => this.changeText() }>改变文本</button>
          </div>
        )
      }
    }

六. React其他案例实现


1.电影列表展示

image-20220902220021317

2.计数器案例

image-20220902220049761

Released under the MIT License.