react中组件的类型可以分为两类,函数式组件和类组件
1. class 类组件
1.1 概念
类组件就是通过生命类并继承React.component而创建的组件
1.2 举例
js
class 自定义类组件名1 extends React.component {
constructor()
render()
}
js
// 创建一个类组件
import react from 'react';
class buttonClassComponent extends React.Component {
// 类组件中两个必须函数: construtor函数和render函数,其他的事件回调函数看着加
constructor() {
super()
this.state = {
n: 0
}
}
render() {
return(
<div>{ this.state.n }</div>
)
}
}
1.3 类组件特性
(1) 生命周期
老的生命周期
在初始化阶段废弃 了:**1.componentWillMounted **
在 更新阶段废弃 了:(props更新)1.componentWillReceiveProps (整个组件更新)2.componentWillUpdate
新生命周期
rust
step1: 首次渲染
constructor -> render -> componentDidMount
step2: 再次渲染:
1. props change -\ (组件是否应该更新钩子) 返回false --结束
2. setState() ----> shouldComponentUpdate -<
3. forceUpdate()-/ 返回true --render-> 更新UI-> componentDidUpdate
step3: 销毁:
componentWillUnmount
注意点1: react组件初始化state、props是在哪个阶段 ? construtor生命周期
注意点2 : react生命周期中对于组件是否更新额外提供了一个钩子:shouComponentUpate,为什么要提供这个钩子?具体的使用场景是什么?
因为有时候我们state的变化会导致组件无效更新,例如:一个函数中对state先+1后-1,本质上最终结果值是不变的,但是值在过程中是变化的。那么因为你使用了setstate就是通知UI更新。显然这是无效更新浪费了性能。所以可以在这个是否组件应该更新的钩子中判断,如果数值不便就不修要更新。
js
onClick = () => {
this.setState(state =>({n:state.n+1})) //进行 +1 操作
this.setState(state =>({n:state.n-1})) //再进行 -1 操作
};
shouldComponentUpdate(newprops, newstate){ //nextprops、nextstate
if(newstate.n === this.state.n){
return false;
}else{
return true;
}
}
注意点3 : 什么时候初始化阶段挂载到真实dom上了?
当执行componentDidMount时候,组件就挂载在真实dom上了。
注意点4 : react为什么要废弃这些生命周期?
新的异步渲染模式以及这些生命周期存在的一些副作用(参考:www.cnblogs.com/hubert-styl...
(2) 类组件中this指向规则
核心规则:
1.this定义在哪个函数中
2.该函数被谁调用
1. render中的this
js
class Demo extends Component{
state = {
count: 0
}
handleClick = () => {
this.setState({count: this.state.count+1})
}
render(){
return(
<div>
{this.state.count}
// 这里直接调用this.state.count,在render函数中使用this
// 按照规则:
// 1. this 使用在render函数中
// 2.render函数被React调用,所以this指向React实例Demo
<div/>
)
}
}
2. click回调函数中的this(是否使用箭头函数)
js
class demo extends React.component {
cosntructor() {
super()
state = { count: 0 }
}
// case1: 回调函数非箭头函数
// 此时,按照规则,this定义在handleclick中,然后handleclick被onclick事件的回调函数使
// 用,回调函数执行时没有被调用者,函数如果找不到调用者,最后就会在顶层对象中调用,也就是this会
// 指向window对象,但由于使用了ES6语法,ES6语法默认采用严格模式,严格模式下,this不会指向
// window,而是undefined,所以才会报错。
handleclick1 = function() {
this.setState({ count: this.state.count + 1 })
}
// case2: 回调函数为箭头函数
// 箭头函数内部的this就是定义时上层作用域的this,handleClick上层作用域是类的构造函数,那么handleClick的this就是构造函数的this,也就是指向Demo类的实例
handleclick2 = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
<div>
<button onClick="{ handleclick1 }">回调函数为非箭头定义 -- 按钮</button>
<button onClick="{ handleclick2 }">回调函数是箭头定义 -- 按钮</button>
</div>
}
}
那么如果我不想使用箭头定义事件回调函数,直接使用function定义的事件回调函数有没有办法解决this指向问题呢?有两种方法:
第一种在onclick绑定的时候使用箭头函数定义,即 onclick =" { () => handleclick() }"
;
js
class Demo extends Component{
state = {
count: 0
}
handleClick = function (){
this.setState({count: this.state.count+1})
}
render(){
return(
<>
{this.state.count}
<button onClick={() => this.handleClick()}>按钮</button>
</>
)
}
}
// 点击按钮正常
第二种还是在onclick绑定的时候使用bind来绑定this,即onclick =" { this.handleclick.bind(this) }"
;
js
class Demo extends Component{
state = {
count: 0
}
handleClick = function (){
this.setState({count: this.state.count+1})
}
render(){
return(
<>
{this.state.count}
<button onClick={this.handleClick.bind(this)}>按钮</button>
</>
)
}
}
// 点击按钮正常
(3) 合成事件机制(SyntheticEvent)
这里是指react框架中,使用的事件机制不是浏览器原生的事件捕获、处于事件阶段、事件冒泡三个阶段,而是react框架中自己根据多个不同浏览器类型中的事件机制实现了自己的一套事件机制,这样就可以抹平 不同浏览器事件机制兼容问题
。
相比于js原生事件的三个阶段,react框架中的合成事件只有两个阶段:事件绑定和事件触发。因此,我们将react这套事件机制称为合成事件。
1.react事件和js原生事件:
js
const handleClick = (e) => {e.preventDefault();}
// 原生事件
<div onclick="handleClick()"></div>
// React合成事件
<div onClick={HandleCilck}></div>
原生事件和合成事件混合使用时候的执行顺序: 输出后的执行顺序:
先执行原生的捕获冒泡-addEventlister参数默认为false-冒泡阶段执行
然后冒泡到document时候执行react合成
最后执行document的事件回调
javascript
原生事件:子元素 DOM 事件监听!
原生事件:父元素 DOM 事件监听!
React 事件:子元素事件监听!
React 事件:父元素事件监听!
原生事件:document DOM 事件监听!
2.为什么要有合成事件
- 对事件进行归类,可以在事件产生的任务上包含不同的优先级
- 提供合成事件对象,抹平浏览器的兼容性差异
3.合成事件机制简述 提供了一种"顶层注册,事件收集,统一触发"的事件机制
- "顶层注册",其实是在root元素上绑定一个统一的事件处理函数
- "事件收集", 事件触发时(实际上是root上的事件处理函数被执行),构造合成事件对象,按照冒泡或捕获的路径去组件中收集真正的事件处理函数
- "统一触发",在收集过程之后,对收集的事件逐一执行,并共享同一个合成事件对象
版本区别
- React16事件绑定到document上
- React17事件绑定到root组件上,有利于多个react版本共存,例如微前端
- event不是原生的,是SyntheticEvent合成事件对象
注意:react合成事件是react重要的基础知识
(4) setState 更新视图
setstate的核心问题:
1.setstate的执行机制?
2.对于数据的更新是 "同步" 还是 "异步"?
答:setState在react自身的合成事件或者钩子函数中被使用的时候就是"异步"执行,而在原生的事件或者
setTimeout
、Promise.resolve().then
中都是同步的。 这里的"异步"是什么意思?"异步"并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的"异步"。
2.函数式组件
2.2 函数组件特性
(1) 无生命周期
(2) useState 更新视图
函数式组件就是一个函数,但是他的返回必须是JSX格式。
js
// 创建一个函数式组件
import React from "react";
function Welcome(props) {
const [count, setCount] = React.useState(0);
return (
<div>
{count}
<h1>Hello, {props.name}</h1>
<button onClick={ () => setCount(count + 1)}>+1</button>
</div>
);
}
export default Welcome
类组件和函数式组件的使用
js
// 主文件,分别引入 函数式组件 和 class类组件
import './App.css';
import Button from './Components/Button.js'
import Welcome from './Components/functionComponent'
function App() {
return (
<div className="App">
<Welcome name="Sara" /> // 函数式组件
<Button></Button>
</div>
);
}
二.类组件和函数式组件的区别
1、setState
和 useState
只在合成事件和钩子函数中是"异步"的,在原生事件和 setTimeout
、Promise.resolve().then
中都是同步的。