02_React面向组件编程--基本使用与理解、组件实例的三大核心属性与事件处理

基本使用与理解、组件实例的三大核心属性与事件处理

    • 一、基本理解与使用
    • [二、组件实例的三大核心属性 1:state](#二、组件实例的三大核心属性 1:state)
      • [1、例子,点击文字切换 凉爽和炎热](#1、例子,点击文字切换 凉爽和炎热)
        • [1.1 复习--原生事件绑定方式](#1.1 复习--原生事件绑定方式)
        • [1.2 复习--类中的方法 this 指向](#1.2 复习--类中的方法 this 指向)
        • [1.3 类的复习---类中添加属性](#1.3 类的复习---类中添加属性)
        • [1.3 例子的简写。state 的简写](#1.3 例子的简写。state 的简写)
      • 2、理解
      • 3、强烈注意
    • [三、组件实例的三大核心属性 2:props](#三、组件实例的三大核心属性 2:props)
      • 1、例子:自定义用来显示一个人员信息的组件
        • [1.1 要求:](#1.1 要求:)
        • [1.2 复习--展开运算符(...)](#1.2 复习--展开运算符(...))
        • [1.3 对传递的属性(props)进行校验](#1.3 对传递的属性(props)进行校验)
        • [1.4 复习-- 类的关键字 static](#1.4 复习-- 类的关键字 static)
        • [1.5 props 简写方式](#1.5 props 简写方式)
        • [1.6 函数式组件使用 props。三大属性中函数式组件仅能使用 props](#1.6 函数式组件使用 props。三大属性中函数式组件仅能使用 props)
      • 2、理解
      • 2、理解
      • 3、作用
      • 4、编码操作
    • [四、组件实例的三大核心属性 3:refs 与事件处理](#四、组件实例的三大核心属性 3:refs 与事件处理)
      • 1、例子
        • [1.1 需求:自定义组件,功能说明如下:](#1.1 需求:自定义组件,功能说明如下:)
        • [1.2 过时 API :String 类型的 Refs](#1.2 过时 API :String 类型的 Refs)
        • [1.3 回调函数形式的 ref](#1.3 回调函数形式的 ref)
        • [1.4 createRef (官方推荐写法)](#1.4 createRef (官方推荐写法))
      • 2、理解
      • 3、编码
      • 4、事件处理

注意:当前 react 版本为 16.8.0

一、基本理解与使用

1、函数式组件

/**

_ 执行了 ReactDOM.render(, ... 之后,发生了什么?

_ 1.React 解析组件标签,找到了 MyComponent 组件

_ 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中

_/

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>函数式组件</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 此处一定要写babel -->
    <script type="text/babel">
      //   1、创建函数式组件
      function MyComponent() {
        console.log(this) // babel 开启严格模式,将自定义的 this 不再是window,而是undefined
        return <h2>我是用函数定义的组件(适用于【简单组件】)的定义</h2>
      }
      // 2、渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
      /**
       * 执行了 ReactDOM.render(<MyComponent/>, .... 之后,发生了什么?
       * 1.React 解析组件标签,找到了 MyComponent 组件
       * 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
       */
    </script>
  </body>
</html>

2、类的复习

总结:

1、类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写

2、如果 A 类继承了 B 类且 A 类中写了构造器,那么 A 类构造器中 super 时必须要调用的

3、类中所定义的方法,都是放在了类的原型对象上,供实例区使用

html 复制代码
<!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>
    <script type="text/javascript">
      // 创建一个类
      class Person {
        // 构造器方法
        constructor(name, age) {
          // 构造器中的this是谁?------ 类的实力对象
          this.name = name
          this.age = age
        }
        // 一般方法:除了构造器以外的自定义方法
        speak() {
          // speak 方法放在了哪里?------类的原型对象上,供实例使用
          // 通过 Person 实例调用 speak 时,speak 中的 this 就是 Person 实例
          console.log(`我叫${this.name},今年${this.age} 岁`)
        }
        // 原型链:当查找一个不存在于自身的原型时,会查找原型,一层一层找下去,直到找到 Object,这就是一个原型链
      }
      // 创建一个实例对象
      let p1 = new Person('tom', 20)
      console.log(p1)
      p1.speak()

      // 创建一个 student 类继承于Person类
      class student extends Person {
        constructor(name, age, grade) {
          super(name, age)
          this.grade = grade
        }
        // 重写从父类继承过来的方法
        speak() {
          console.log(
            `我叫${this.name},今年${this.age} 岁,我正在读${this.grade}`,
          )
        }
        study() {
          // speak 方法放在了哪里?------类的原型对象上,供实例使用
          // 通过 student 实例调用 speak 时,speak 中的 this 就是 student 实例

          console.log('我学习很用心')
        }
      }
      let s1 = new student('小张', 15, '九年级')
      console.log(s1)
      s1.speak()
      s1.study()
    </script>
  </body>
</html>

3、类式组件

_ 执行了 ReactDOM.render(, ... 之后,发生了什么?

_ 1.React 解析组件标签,找到了 MyComponent 组件

_ 2.发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到 原型上的 render 方法。

_ 3.将 render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>类式组件</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 此处一定要写babel -->
    <script type="text/babel">
      //   1、创建类式组件
      class MyComponent extends React.Component {
        render() {
          // render 是放在哪里的?------ MyComponent 的原型对象上,供实例使用
          //  render  中的this 是谁?------ MyComponent 的实例对象。MyComponent 组件的实例对象
          return <h2>我是用类定义的组件(适用于【复杂组件】)的定义</h2>
        }
      }
      // 2、渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
      /**
       * 执行了 ReactDOM.render(<MyComponent/>, .... 之后,发生了什么?
       * 1.React 解析组件标签,找到了 MyComponent 组件
       * 2.发现组件是使用类定义的,随后new 出来该类的实例,并通过该实例调用到 原型上的 render 方法。
       * 3.将render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
       */
    </script>
  </body>
</html>

4、简单组件和复杂组件

简单组件:没有状态

复杂组件:有状态(state)的组件

例子:

人 状态 影响 行为

组件 状态 驱动 数据

二、组件实例的三大核心属性 1:state

1、例子,点击文字切换 凉爽和炎热

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>切换天气</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class Weather extends React.Component {
        // 构造器调用几次?------ 1次
        constructor(props) {
          console.log('constructor')
          super(props)
          // 借用构造器初始化 状态
          this.state = {
            isHot: false,
            wind: '微风',
          }
          // bind 改变 this 的指向并且返回一个新函数
          this.changeWeather = this.changeWeather.bind(this) // 解决类中方法局部严格模式导致 this 为 undefined 问题
        }
        // render调用几次?------ 1 + n 次, 1 是初始化的那次,  n 是状态更新的次数
        render() {
          console.log('render')
          // 读取状态
          let { isHot, wind } = this.state
          console.log(this, this.state)
          return (
            <h1 onClick={this.changeWeather}>
              今天天气很{isHot ? '炎热' : '凉爽'}, {wind}
            </h1>
          )
        }
        // changeWeather调用几次?------ 点几次调几次
        changeWeather() {
          // changeWeather 放在那里?------ Weather 的原型对象,供实例使用
          // 由于changeWeather 是作为 onClick 的回调,所以不是通过实例调用的,是直接调用
          // 类中的方法 默认开启了局部的严格模式,所以 changeWeather 中的 this 是 undefined
          // console.log('点击文字', this) // 为什么this 是 undefined?------类中的方法 默认开启了局部的严格模式,所以 changeWeather 中的 this 是 undefined

          // 获取原来的 isHot 的值
          let { isHot } = this.state

          // 严重注意:状态(State)里面的数据不能直接更改, 要借助一个内置的 API 去更改
          // this.state.isHot = !isHot // 这行就是直接更改!这是错误写法

          // 严重注意: 状态(state)必须通过 setState 进行更改,且更新是一种合并,不是替换
          this.setState({ isHot: !isHot })
          console.log(isHot, this)
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<Weather />, document.getElementById('test'))
    </script>
  </body>
</html>

为什么要写构造器?------ 为了初始化状态和改变 this 的指向
setState 更改数据时候是合并还是替换?------ 是合并,不是替换
constructor 构造器调用几次?------ 1 次
render 调用几次?------ 1 + n 次, 1 是初始化的那次, n 是状态更新的次数
changeWeather 调用几次?------ 点几次调几次
类中的方法 默认开启了局部的严格模式, this 为 undefined

onClick={changeTitle} :指定函数,点击的时候 react 帮你调用函数,不可以加小括号,如果添加了页面渲染完毕 react 就会直接调用函数

1.1 复习--原生事件绑定方式
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>原生事件绑定</title>
  </head>
  <body>
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button onclick="demo()">按钮3</button>
    <script type="text/javascript">
      let btn1 = document.getElementById('btn1')
      btn1.addEventListener(
        'click',
        () => {
          alert('按钮1 被点击了')
        },
        false,
      )
      let btn2 = document.getElementById('btn2')
      btn2.onclick = () => {
        alert('按钮2 被点击了')
      }

      function demo() {
        alert('按钮3 被点击了')
      }
    </script>
  </body>
</html>
1.2 复习--类中的方法 this 指向

类中所定义的方法 局部都开启了严格模式

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>3_类中的方法this 指向.html</title>
  </head>
  <body>
    <script type="text/javascript">
      class Person {
        constructor(name, age) {
          this.name = name
          this.age = age
        }
        // 类中所定义的方法 局部都开启了严格模式
        speak() {
          // speak 方法放在了哪里?------ 类的原型对象上,供实例使用
          // 通用 Person 实例调用 speak 时,speak 中的 this 就是 Person 实例
          console.log(this)
        }
      }
      let p1 = new Person('Tom', 20)
      p1.speak() // 通过实例调用 speak 方法

      const x = p1.speak // 函数的直接调用
      x() // undefined : 类中所定义的方法 局部都开启了严格模式,直接调用 this 是 undefined
    </script>
  </body>
</html>
1.3 类的复习---类中添加属性
html 复制代码
<!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>
    <script type="text/javascript">
      class Car {
        constructor(name, price) {
          this.name = name
          this.price = price
        }
        // 类中可以直接写赋值语句,如下的代码的含义是: 给Car 的实例对象添加一个属性,名为 wheel,值为 4
        wheel = 4
      }
      let c1 = new Car('奔驰C63', 199)
      let c2 = new Car('奔驰111C63', 100)
      console.log(c2)
      console.log(c1)
    </script>
  </body>
</html>
1.3 例子的简写。state 的简写
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>state 的简写</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class Weather extends React.Component {
        state = {
          isHot: false,
          wind: '微风',
        }

        render() {
          // console.log(this)
          let { isHot, wind } = this.state
          return (
            <h1 onClick={this.changeWeather}>
              今天天气很{isHot ? '炎热' : '凉爽'}, {wind}
            </h1>
          )
        }
        // 以下代码相当于 changeWeather = this.changeWeather.bind(this)
        // 此处不要写普通函数。需要写箭头函数:因为箭头函数没有自己的this,会找外层的 this
        changeWeather = () => {
          console.log(this)
          let { isHot } = this.state
          this.setState({ isHot: !isHot })
        }
      }
      // 2、渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById('test'))
    </script>
  </body>
</html>

2、理解

1)state 是组件对象最重要的属性,值是对象(可以包含多个 key-value 的组合)------ 因为 setState 更改状态的时候接收的是 对象

2)组件被称为"状态机", 通过更新组件的 state 来更新对应的页面显示(重新渲染组件)

3、强烈注意

1)组件中 render 方法中的 this 为组件实例对象

2)组件自定义的方法中的 this 为 undefined,如何解决?

a、强制绑定 this:通过函数对象的 bind()

b、箭头函数

3)状态数据:不能直接修改或更新

三、组件实例的三大核心属性 2:props

1、例子:自定义用来显示一个人员信息的组件

1.1 要求:

1)姓名必须指定,且为字符串类型

2)性别为字符串类型,如果性别没有指定,默认为男

3)年龄必须指定,且为数字类型

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>props基本使用</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test1"></div>
    <div id="test2"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>
    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class Person extends React.Component {
        render() {
          console.log(this)
          let { name, gender, age } = this.props
          return (
            <ul>
              <li>姓名:{name}</li>
              <li>性别:{gender}</li>
              <li>年龄:{age}</li>
            </ul>
          )
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      const p = { name: 'Tom54', gender: '男', age: 20 }
      // {...p} 一般来说展开运算符不能展开对象,但是 在 babel 和react 中可以用来展开对象作批量属性到组件上
      ReactDOM.render(<Person {...p} />, document.getElementById('test'))
      ReactDOM.render(
        <Person name="jerry" gender="女" age={18} />,
        document.getElementById('test1'),
      )
      ReactDOM.render(
        <Person name="jack" gender="男" age={5} />,
        document.getElementById('test2'),
      )
    </script>
  </body>
</html>
1.2 复习--展开运算符(...)

用法:

1)展开一个数组

2)连接数组

3)在函数中使用

4)字面量的形式复制一个对象,是深克隆

5)合并对象(复制对象的同时修改属性)

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script type="text/javascript">
      let arr = [1, 2, 3, 4, 5]
      console.log(...arr) // 1、展开一个数组
      let arr2 = [11, 12, 14, 16]
      let arr3 = [...arr, ...arr2] // 2、连接数组
      // 3、在函数中使用
      function sum(...numbers) {
        console.log('@', numbers)
        // 实现方式1:
        // let total = 0
        // numbers.forEach((el) => {
        //   total += el
        // })
        // return total
        //实现求和方法2
        return numbers.reduce((preValue, currentValue) => {
          return preValue + currentValue
        })
      }
      console.log(sum(1, 2, 5)) //
      let p = { name: 'Tom', page: 15 }
      let p2 = { ...p } // 4、字面量的形式复制一个对象,是深克隆
      // console.log(...p) // 报错,展开运算符不能展开对象
      p.name = 'jack'
      console.log(p2.name)
      //5、合并对象(复制对象的同时修改属性)
      let p3 = { ...p, name: 'Jerry' }
      console.log(p3.name)
    </script>
  </body>
</html>
1.3 对传递的属性(props)进行校验

React 15.x 之前 React.PropTypes 有维护

React 16.x 后 React.PropTypes 被废弃

html 复制代码
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>props基本使用</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test1"></div>
    <div id="test2"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class Person extends React.Component {
        render() {
          let { name, gender, age } = this.props
          return (
            <ul>
              <li>姓名:{name}</li>
              <li>性别:{gender}</li>
              <li>年龄:{age + 1}</li>
            </ul>
          )
        }
      }
      // react 识别propTypes属性就是加规则
      // 对标签属性进行类型、必要性的限制
      Person.propTypes = {
        name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
        gender: PropTypes.string, // 性别为字符串类型
        age: PropTypes.number, //年龄为数字类型
        speak: PropTypes.func, // 限制 speak 为函数
      }
      // 指定默认标签属性值
      Person.defaultProps = {
        gender: '男',
        age: 18,
      }

      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      const p = { name: 'Tom54', age: 20 }
      ReactDOM.render(<Person {...p} />, document.getElementById('test'))
      ReactDOM.render(
        <Person name={12} gender="女" age={18} speak="1" />,
        document.getElementById('test1'),
      )
      ReactDOM.render(
        <Person name="jack" gender="男" />,
        document.getElementById('test2'),
      )

      function speak() {
        console.log('说话了')
      }
    </script>
  </body>
</html>
1.4 复习-- 类的关键字 static
javascript 复制代码
class Car {
  constructor(name, price) {
    this.name = name
    this.price = price
  }
  // 类中可以直接写赋值语句,如下的代码的含义是: 给Car 的实例对象添加一个属性,名为 wheel,值为 4
  wheel = 4
  a = 1
  static demo = 100 // 添加一个属性给 类
}
let c1 = new Car('奔驰C63', 199)
console.log(c2)
console.log(c2.demo())
1.5 props 简写方式
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>props基本使用</title>
  </head>
  <body>
    <div id="test"></div>
    <div id="test1"></div>
    <div id="test2"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class Person extends React.Component {
        render() {
          let { name, gender, age } = this.props
          // this.props.name = '哈哈哈'// 此行代码会报错,因为 props 是只读的
          return (
            <ul>
              <li>姓名:{name}</li>
              <li>性别:{gender}</li>
              <li>年龄:{age + 1}</li>
            </ul>
          )
        }
        // 对标签属性进行类型、必要性的限制
        static propTypes = {
          name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
          gender: PropTypes.string, // 性别为字符串类型
          age: PropTypes.number, //年龄为数字类型
          speak: PropTypes.func, // 限制 speak 为函数
        }

        // 指定默认标签属性值
        static defaultProps = {
          gender: '男',
          age: 18,
        }
      }

      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      const p = { name: 'Tom54', age: 20 }
      ReactDOM.render(<Person {...p} />, document.getElementById('test'))
      ReactDOM.render(
        <Person name={12} gender="女" age={18} speak={speak} />,
        document.getElementById('test1'),
      )
      ReactDOM.render(
        <Person name="jack" gender="男" />,
        document.getElementById('test2'),
      )

      function speak() {
        console.log('说话了')
      }
    </script>
  </body>
</html>

构造器 constructor

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super(props), 否则, this.props 在构造函数中可能会出现未定义的 bug

构造器是否接收 props,是否传递给 super, 取决于:是否希望在构造器中通过 this 访问 props (几乎不用)

1.6 函数式组件使用 props。三大属性中函数式组件仅能使用 props
html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>简写</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      function Person(props) {
        let { name, gender, age } = props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{gender}</li>
            <li>年龄:{age + 1}</li>
          </ul>
        )
      }
      Person.propTypes = {
        name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
        gender: PropTypes.string, // 性别为字符串类型
        age: PropTypes.number, //年龄为数字类型
      }
      Person.defaultProps = {
        gender: '男',
        age: 18,
      }

      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      const p = { name: 'Tom', age: 15, gender: '女' }
      ReactDOM.render(<Person {...p} />, document.getElementById('test'))
    </script>
  </body>
</html>

2、理解

1)每个组件对象都会有 props(properties 的简写)属性

2)组件标签的所有属性都保存在 props 中

2、理解

1)每个组件对象都会有 props(properties 的简写)属性

2)组件标签的所有属性都保存在 props 中

3、作用

1)通过标签属性从组件外向组件内传递变化的数据

2)注意:组件内部不要修改 props 数据

4、编码操作

1)内部读取某个属性值

javascript 复制代码
this.props.name

2)对 props 中的属性值进行类型限制和必要性限制
第一种方式 (React v15.5 开始已经弃用)

javascript 复制代码
Person.propTypes = {
  name: React.PropTyes.string,
}

第二种方式 (新)

使用 prop-types 库进行限制(需要引入 prop-types 库)

javascript 复制代码
Person.propTypes = {
  name: React.PropTyes.string,
}

3)扩展属性:将对象的所有属性通过 props 传递

javascript 复制代码
<Person {...param} />

4)默认属性值

javascript 复制代码
Person.defaultProps = {
  gender: '男',
  age: 18,
}

5)组件类的构造函数(项目中基本不用)

javascript 复制代码
constructor(props) {
  super(props)
  console.log(props) // 打印所有属性
}

四、组件实例的三大核心属性 3:refs 与事件处理

1、例子

1.1 需求:自定义组件,功能说明如下:

1)点击按钮,提示第一个输入框中的值

2)当第 2 个输入框失去焦点时,提示这个输入框中的值

1.2 过时 API :String 类型的 Refs

string 类型的 ref 存在一些效率问题。已过时会在未来的版本被移除

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>refs 与事件处理</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class MyComponent extends React.Component {
        render() {
          return (
            <div>
              <input ref="input1" type="text" placeholder="点击按钮提示数据" />
              &nbsp; &nbsp;
              <button onClick={this.showData}>点我提示左侧的数据</button>
              &nbsp; &nbsp;
              <input
                ref="input2"
                type="text"
                placeholder="失去焦点提示数据"
                onBlur={this.blurInput}
              />
            </div>
          )
        }
        // 展示左侧输入框的数据
        showData = () => {
          console.log('点击按钮提示数据', this.refs.input1.value)
        }
        blurInput = () => {
          console.log('失去焦点提示数据:', this.refs.input2.value)
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
    </script>
  </body>
</html>
1.3 回调函数形式的 ref

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null, 然后第二次会传输参数 DOM 元素。

这是因为在每次渲染时创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。

通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的

内联函数方式

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>refs 与事件处理</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class MyComponent extends React.Component {
        render() {
          return (
            <div>
              <input
                ref={(currentNode) => (this.input1 = currentNode)}
                type="text"
                placeholder="点击按钮提示数据"
              />
              &nbsp; &nbsp;
              <button onClick={this.showData}>点我提示左侧的数据</button>
              &nbsp; &nbsp;
              <input
                ref={(currentNode) => (this.input2 = currentNode)}
                type="text"
                placeholder="失去焦点提示数据"
                onBlur={this.blurInput}
              />
            </div>
          )
        }
        // 展示左侧输入框的数据
        showData = () => {
          let { input1 } = this
          console.log(input1.value)
        }
        blurInput = () => {
          let { input2 } = this
          console.log(input2.value)
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
    </script>
  </body>
</html>

回调函数的 ref 中 回调函数执行次数?------ 更新(触发 render 时)过程中会被执行两次,第一次传入参数 null, 然后第二次会传输参数 DOM 元素
jsx 中注释代码{//}**

通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>refs 与事件处理</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class MyComponent extends React.Component {
        state = {
          isHot: true,
        }
        render() {
          let { isHot } = this.state
          return (
            <div>
              <h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
              {/*<input
                ref={(currentNode) => {
                  this.input1 = currentNode
                  console.log('@@', currentNode)
                }}
                type="text"
                placeholder="点击按钮提示数据"
              />*/}
              <input
                ref={this.saveInput}
                type="text"
                placeholder="点击按钮提示数据"
              />
              &nbsp; &nbsp;
              <button onClick={this.showData}>点我提示数据</button>
              &nbsp; &nbsp;
              <button onClick={this.changeWeather}>点我切换天气</button>
            </div>
          )
        }
        saveInput = (currentNode) => {
          this.input1 = currentNode
          console.log(this)
        }
        // 展示左侧输入框的数据
        showData = () => {
          let { input1 } = this
          console.log(input1.value)
        }
        // 切换天气
        changeWeather = () => {
          let { isHot } = this.state
          this.setState({ isHot: !isHot })
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
    </script>
  </body>
</html>
1.4 createRef (官方推荐写法)

React.createRef 调用后可以返回一个容器,该容器可以存储被 ref 所表示的节点,该容器是 "专人专用" 的(只能存放一个 ref),后面的会覆盖前面的

缺点:每一个 ref 都需要创建容器

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>createRef</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class MyComponent extends React.Component {
        /**
         * React.createRef调用后可以返回一个容器,该容器可以存储被 ref 所表示的节点
         */
        myRef = React.createRef()
        myRef2 = React.createRef()
        render() {
          return (
            <div>
              <input
                ref={this.myRef}
                type="text"
                placeholder="点击按钮提示数据"
              />
              &nbsp; &nbsp;
              <button ref={this.myRef} onClick={this.showData}>
                点我提示数据
              </button>
              &nbsp; &nbsp;
              <input
                ref={this.myRef2}
                type="text"
                onBlur={this.showData2}
                placeholder="失去焦点提示数据"
              />
            </div>
          )
        }
        // 展示左侧输入框的数据
        showData = () => {
          let { current } = this.myRef
          console.log(current.value)
        }
        showData2 = () => {
          let { current } = this.myRef2
          console.log(current.value)
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
    </script>
  </body>
</html>

2、理解

组件内的标签可以定义 ref 属性来标识自己

3、编码

1)字符串形式的 ref

javascript 复制代码
<input ref="input2" type="text" />

2)回调形式的 ref

javascript 复制代码
<input
  ref={(currentNode) => {
    this.input1 = currentNode
  }}
/>

3)createRef 创建 ref 容器

javascript 复制代码
myRef = React.createRef()
<input
  ref={this.myRef}
  type="text"
/>

需要避免过度使用 ref

4、事件处理

1)通过 onXxx 属性指定事件处理函数(注意大小写)

a、React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件 ------ 为了更好的兼容性

b、React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)------ 为了高效

事件委托的原理是事件冒泡

2)通过 event.target 得到发生事件的 DOM 元素对象 ------ 不要过度使用 ref

当发生事件的元素就是操作的元素,就可以省略 ref , 使用 event.target 获取即可

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>createRef</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入 react 核心库 -->
    <script type="text/javascript" src="../js/react.development.js"></script>
    <!-- 引入 react-dom,用于支持 react 操作DOM -->
    <script
      type="text/javascript"
      src="../js/react-dom.development.js"
    ></script>
    <!-- 引入babel, 用于将 jsx 转为 js -->
    <script type="text/javascript" src="../js/babel.min.js"></script>

    <!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
    <script type="text/javascript" src="../js/prop-types.js"></script>

    <!-- 此处一定要写babel -->
    <script type="text/babel">
      // 1、创建组件
      class MyComponent extends React.Component {
        /*
        1)通过 onXxx 属性指定事件处理函数(注意大小写)
          a、React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件
          b、React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
        2)通过 event.target 得到发生事件的 DOM 元素对象

        */
        // 创建 ref 容器
        myRef = React.createRef()
        myRef2 = React.createRef()
        render() {
          return (
            <div>
              <input
                ref={this.myRef}
                type="text"
                placeholder="点击按钮提示数据"
              />
              &nbsp; &nbsp;
              <button ref={this.myRef} onClick={this.showData}>
                点我提示数据
              </button>
              &nbsp; &nbsp;
              <input
                type="text"
                onBlur={this.showData2}
                placeholder="失去焦点提示数据"
              />
            </div>
          )
        }
        // 展示左侧输入框的数据
        showData = () => {
          let { current } = this.myRef
          console.log(current.value)
        }
        showData2 = (event) => {
          console.log(event.target.value)
        }
      }
      // 2、渲染组件到页面
      // ReactDOM.render(组件,容器)
      ReactDOM.render(<MyComponent />, document.getElementById('test'))
    </script>
  </body>
</html>
相关推荐
蟾宫曲2 小时前
在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)
前端·npm·vue3·vite·element-plus·计时器
秋雨凉人心2 小时前
简单发布一个npm包
前端·javascript·webpack·npm·node.js
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
qq13267029402 小时前
运行Zr.Admin项目(前端)
前端·vue2·zradmin前端·zradmin vue·运行zradmin·vue2版本zradmin
魏时烟4 小时前
css文字折行以及双端对齐实现方式
前端·css
哥谭居民00014 小时前
将一个组件的propName属性与父组件中的variable变量进行双向绑定的vue3(组件传值)
javascript·vue.js·typescript·npm·node.js·css3
踢足球的,程序猿5 小时前
Android native+html5的混合开发
javascript
2401_882726485 小时前
低代码配置式组态软件-BY组态
前端·物联网·低代码·前端框架·编辑器·web
web130933203985 小时前
ctfshow-web入门-文件包含(web82-web86)条件竞争实现session会话文件包含
前端·github
胡西风_foxww5 小时前
【ES6复习笔记】迭代器(10)
前端·笔记·迭代器·es6·iterator