likes
comments
collection
share

邂逅React

作者站长头像
站长
· 阅读数 27

一、React的介绍和特点

1.1 React的介绍

1.2 React的特点

1.2.1 声明式编程

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

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

1.2.2 组件化开发

  • 组件化开发页面目前前端的流行趋势,每一个复杂的界面都能拆分成一个个小的组件

1.2.3 多平台适配

  • 2013年,React发布之初主要是开发Web页面

  • 2015年,Facebook推出了ReactNative,用于开发移动端跨平台

  • 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序

二、React开发依赖分析

界面上显示一个按钮和一段文本,点击按钮切换文本

2.1 React 依赖三个包

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

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

    • web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
    • native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Buttom,iOS中的UIButton)
  • babel:将jsx转换成React代码的工具

2.2 Babel 和 React 的关系

  • babel是什么?

    • 是目前前端使用非常广泛的编译器、转移器

    • 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它;那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法

  • React 和 Babel的关系

    • 默认情况下开发React其实可以不使用babel

    • 前提是我们自己使用 React.createElement 来编写源代码,但是它编写的代码非常的繁琐和可读性差。

    • 我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成React.createElement

  • React依赖注入

    • 直接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>
      
    • 下载后,添加本地依赖

    • 通过npm管理

三、React初体验

3.1 Hello World

  • 在编写React的script代码中,必须添加 type="text/babel",作用是可以让babel解析jsx的语法

  • React 18 之前

    • ReactDOM.render(<h2>Hello World</h2>, document.querySelector("#root"))
    • 引入React 18的依赖会报错 邂逅React
  • React 18

    • 通过 ReactDOM.createRoot() 选择一个根
    • ReactDOM. createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个根中
    • 参数:将渲染的内容,挂载到哪一个HTML元素上
    <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 type="text/babel">
    
    // 渲染 Hello World
    // React 18 之前:React.DOM.render
    // ReactDOM.render(<h2>Hello World</h2>, document.querySelector("#root"))
    
    // React18之后
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    root.render(<h2>Hello World</h2>)
    
    const app = ReactDOM.createRoot(document.querySelector("#app"))
    app.render(<h2>你好 世界</h2>)
    

3.2 Hello React 案例

一个按钮,一段文本,点击按钮修改文本

  • 注意:React 修改内容之后需要重新渲染界面
  • 基本实现
    const root = ReactDOM.createRoot(document.querySelector("#root"))
    
    // 将文本定义成一个变量
    let message = "Hello World"
    
    // 监听按钮的点击
    const btnClick = () => {
      // 修改数据
      message = "Hello React"
    
      // 重新渲染
      rootRender()
    }
    
    // 第一次渲染
    rootRender()
    
    function rootRender() {
      root.render((
        <div>
          <h2>{message}</h2>
          <button onClick={btnClick}>修改文本</button>
        </div>
      ))
    }
    

四、组件化初体验

4.1 用类的方式封装组件

  • 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component

  • 实现当前组件的render函数

    • render当中返回的jsx内容,就是之后React会帮助我们渲染的内容

4.2 数据依赖

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

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

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

  • 参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中

    • 通过在构造函数中 this.state = {定义的数据}

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

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

4.3 事件绑定

  • 事件绑定中的this

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

    • 为什么是undefined呢?

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

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

    • 那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined(在类中的方法,默认在严格模式,严格模式 this 指向 undefined

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

    • 需要在传入函数时,给这个函数直接绑定this(通过bind

    • 类似于下面的写法: <button onClick={this.changeText.bind(this)}>改变文本</button>

4.4 代码实现

// 类组件
class App extends React.Component {
  // 组件数据
  constructor() {
    super()

    this.state = {
      message: "Hello World"
    }

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

  // 实例方法
  changeText() {
    // setState 是继承 父类中的方法
    // 内部完成两件事情:1.修改state中的message 2.自动重新执行render函数
    this.setState({
      message: "Hello React"
    })
  }

  // 渲染内容
  render() {
    // console.log(this); // 指向 当前 App
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={this.changeText.bind(this)}>修改文本</button>  
      </div>
    )
  }
}

// 将组件渲染到界面上
const root = ReactDOM.createRoot(document.querySelector("#root"))

root.render(<App />)
  • 效果 邂逅React

五、demo 练习

5.1 电影列表

  • 列表循环使用 数组的 map 方法
// 创建root
const root = ReactDOM.createRoot(document.querySelector("#root"))

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

    this.state = {
      movies: ["大话西游", "独行月球", "流浪地球", "飞驰人生", "火星救援"]
    }
  }

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

// 渲染组件
root.render(<App/>)

5.2 计数器

const root = ReactDOM.createRoot(document.querySelector("#root"))

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      counter: 100
    }

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

  increment() {
    this.setState({
      counter: this.state.counter + 1
    })
  }

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

  render() {

    const { counter } = this.state

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

root.render(<App />)