文章目录
组件生命周期
- 组件的生命周期: 组件从创建到挂在到页面中运行,再到组件不用时卸载的过程
- 生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数
- 钩子函数的作用: 为开发人员在不同阶段操作组件提供了时机
只有类组件,才会有生命周期
生命周期三个阶段
创建时
consturctor
=>render
=>componentDidMount
钩子函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时,最先执行 | 1. 初始化state 2. 为时间处理程序绑定this |
render | 每次组件渲染都会触发 | 渲染UI(注意: 不能调用setState()) |
componentDidMount | 组件挂在(完成DOM渲染)后 | 1. 发送网络请求 2. DOM操作 |
更新时
render
===>componentDidUpdate
当调用New props组件接收到props属性
,setState()
,forceUpdate()
时候会触发render
的钩子函数
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染UI(与挂在阶段是同一个render) |
componentDidUpdate | 组件更新(完成DOM渲染)后 | 1. 发送网络请求 2. DOM操作 注意: 如果要setState()必须放在一个if条件中 |
卸载时
组件从页面中消失
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如: 清理定时器等) |
render-props和高阶组件
复用相似的功能(联想函数封装)
render props模式
- 创建Mouse组件,在组件中提供复用的状态逻辑代码(1. 状态 2.操作状态的方法)
- 将要复用的状态作为props.render(state)方法的参数,暴露到组件外部
- 使用props.render()的返回值作为将要渲染的内容
import React from 'react';
import ReactDOM from 'react-dom';
class Mouse extends React.Component {
state= {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
//监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.render(this.state)
}
}
class App extends React.Component {
render(){
return (
<div>
<h1>render props 模式</h1>
<Mouse render={(mouse)=>{
return <p>鼠标位置: {mouse.x} {mouse.y}</p>
}}/>
</div>
)
}
}
//渲染组件
ReactDOM.render(<App />,document.getElementById('root'))
使用children代替render属性
<Mouse>
{({x,y}) => <p>鼠标的位置: {x} {y}</p>}
</Mouse>
//组件内部
this.props.children(this.state)
高阶组件模式(HOC)
包装模式去实现状态逻辑复用
- 高阶组件,是一个函数,接收要包装的组件,返回增强后的组件
- 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件
const EnhancedComponent = withHOC(组件名称)
使用步骤
- 创建函数,名称约定以with开头
- 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
- 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
- 调用高阶组件,传入要增强的组件,通过返回值拿到要增强后的组件,并将其渲染到页面中
function withMouse(WrappedComponent){
class Mouse extends React.Component {
return Mouse
}
}
示例
import React from 'react';
import ReactDOM from 'react-dom';
//创建高阶组件
function withMouse(WrappedComponent){
class Mouse extends React.Component{
//鼠标状态
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
//控制逻辑
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
componentWillUnmount(){
window.removeEventListener('mousemove',this.handleMouseMove)
}
render(){
return <WrappedComponent {...this.state}></WrappedComponent>
}
}
return Mouse
}
//用来测试高阶组件
const Position = props => (
<p>
鼠标当前位置: (x: {props.x},y: {props.y})
</p>
)
const MousePosition = withMouse(Position)
class App extends React.Component {
render(){
return (
<div>
<h1>render props 模式</h1>
<MousePosition />
</div>
)
}
}
//渲染组件
ReactDOM.render(<App />,document.getElementById('root'))
设置displayName属性
用来区分组件的名称
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
传递props
- 问题: props丢失
- 原因: 高阶组件没有往下传递props
- 解决方式: 渲染WrappedComponent时,将state和this.props一起传递给组件
- 传递方式:
<WrappedComponent {...this.state} {...this.props} />
React原理
setState()的说明
- setState()是异步操作的
- 在使用该语法的时候,后面的setState()不要依赖于前面的setState()
- 之后触发一次render()进行渲染
推荐语法
setState((state,props) => {})
这种语法也是异步的形式,state是更新的
this.setState((state,props) => {
return {
count: state.count + 1
}
})
console.log(this.state.count)
第二个参数
在状态更新(页面完成重新渲染)后立即执行某个操作
setState(updater,[callback])
this.setState(
(state,props)=>{},
()=>{
document.title = '更新state后的标题: ' + this.state.count
}
)
JSX语法的转化过程
- JSX仅仅是createElement方法的语法糖(简化语法)
- JSX语法被@babel/preset-react插件编译为createElement() 方法
- React元素: 是一个对象,用来描述你希望在屏幕上看到的内容
JSX语法 ====> createElement() ====> React元素
组件更新机制
- setState()的两个作用:
- 修改state
- 更新组件(UI)
父组件重新渲染时,也会重新渲染子组件,但指挥渲染当前组件子树(当前组件及其所有组件)
组件性能优化
- 减轻state: 只存储根组件渲染相关的数据(比如: count / 列表数据 / loading等)
不同做渲染的数据不要放在state中,比如定时器id等
避免比不要的重新渲染
- 组件更新机制: 父组件更新会引起子组件也被更新
问题:
子组件没有任何变化的情况下也会重新渲染
解决方法:
使用钩子函数shouldComponentUpdate(nextProps,nextState)
触发时机:
更新阶段的钩子函数,组件重新渲染执行(shouldComponentUpdate ----> render)
class Hello extends Component {
shouldComponentUpdate(nextProps,nextState) {
//根据条件,决定是否重新渲染组件
return nextState.number !== this.state.number
}
}
纯组件
定义冲纯组件只需要将React.Component
换成React.PureComponent
区别:
PureComponent内部自动实现了shouldComponentUpdate钩子,不需要手动比较
- 说明: 纯组件内部的对比时
shallow comare
(浅层对比) - 对于值类型来说: 比较两个值是否相同(直接赋值即可)
- 对于引用类型来说: 值比较对象的引用(地址)是否相同。
state或props中属性值为引用类型时,应该创建新数据,不要直接修改原来数据!
const newObj = { ...this.state.obj,number: Math.floor(Math.random() * 3) }
this.setState(()=>{
return {
obj: newObj
}
})
虚拟DOM和Diff算法
- React更新视图的思想是: 只要state变化就会重新渲染视图
- 部分更新,只更新变化的地方
虚拟DOM
本质上就是液体个JS对象,用来描述希望在屏幕上看到的内容(UI)
Diff算法
- 初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象(树)
- 根据虚拟DOM生成真正的DOM,渲染到页面中
- 当数据变化后(setState()),重新根据的数据,创建新的虚拟DOM对象(树)
- 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
- 最终,React只将变化的内容跟新(patch)到DOM中,重新渲染页面
本文含有隐藏内容,请 开通VIP 后查看