React框架之组件-02

一、Context

作用:跨组件传递数据
使用步骤:
1 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件
const {Provider,Consumer}=React.createContext
2 使用Provider组件作为父节点

    
3 设置value属性,表示要传递的数据 4 调用Consumer组件接收数据 {data = >data参数表示接收到的数据--{data}} 总结: 1 如果两个组件是嵌套多层可以使用Context实现组件通信 2 Context提供了两个组件:Provider和Consumer 3 Provider组件:用来提供数据 4 Consumer组件:用来消费数据

二、props深入

2.1、children属性

children属性:表示组件的子节点,当组件标签有子节点时,props就会有该属性
children属性与普通的props一样,值可以是任意值
function Hello(props){
    return (
组件的子节点:{props.children}
) } 我是子节点

2.2、props校验

props校验:允许在创建数组的时候,就指定props的类型、格式等
作用:捕获使用组件时应为props导致的错误,给出明确的错误提示,增加组件的健壮性
使用步骤:
1 安装包prop-types(yarn add prop-types/npm i props-types)
2 导入prop-types包
3 使用组件名.propTypes={}来给组件的props添加校验规则
4 校验规则通过PropTypes对象来指定
import PropTypes from 'prop-types'
function App(props){
    return (

hello,{props.colors}

) } App.propTypes={ // 约定colors属性为array类型 // 如果类型不对,则报出明确的错误,便于分析错误原因 colors: PropTypes.array } 约束规则: 1 常见类型:array,bool,func,number,object,string 2 React元素类型:element 3 必填项:isRequired 4 特点结构的对象:shape({}) // 常见类型 optionalFunc:PropTypes.func, // 必选 requiredFunc:PropTypes.func.isRequired // 特点结构的对象 optionalObjectWithShape:PropTypes.shape({ color:PropType.string, fontSize:PropTypes.number })

2.3、props的默认值

作用:给props设置默认值,在未传入props时生效
function App(props){
    return (
此次展示props的默认值:{props.pageSize}
) } // 设置默认值 App.defaultProps = { pageSize: 123 } // 不传入pageSize属性

三、组件的生命周期

3.1、组件的生命周期概述

意义:组件的生命周期有助于理解组件的运行方式、完成更复杂的组件功能、分析组件错误原因等
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程
生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数
钩子函数的作用:为开发人员在不同阶段操作组件提供了时机
只有类组件才有生命周期

3.2、生命周期的三个阶段

3.2.1、创建时(挂载阶段)

执行时机:组件创建时(页面加载时)
执行顺序:constructor()-->render()-->componentDidMount

钩子函数 触发时机 作用
constructor 创建组件时,最先执行 初始化state、为事件处理程序绑定this
render 每次组件渲染都会触发 渲染UI(注意:不能调用setState())
componentDidMount 组件挂载(完成DOM渲染)后 发送网络请求、DOM操作

3.2.2、更新时(更新阶段)

执行时机:1.setState() 2.forceUpdate() 3.组件接收到新的props
说明:以上三者任意一种变化,组件就会重新渲染
执行顺序:render()--->componentDidUpdate()

钩子函数  触发时机 作用
render 每次组件渲染都会触发 渲染UI(与挂载阶段是同一个render)
componentDidUpdate 组件更新(完成DOM渲染)后 发送网络请求、DOM操作,注意:如果要setState()必须放在一个if条件中

3.2.3、卸载时(卸载阶段)

执行时机:组件从页面中消失
钩子函数:componentWillUnmount
触发时机:组件卸载(从页面中消失)
作用:执行清理工作(比如:清理定时器等)

四、render-props和高阶组件

4.1、React组件复用概述

思考:如果两个组件中的部分功能相似或相同,该如何处理?
处理方式:复用相似的功能(联想函数封装)
复用什么?1.state 2.操作state的方法(组件状态逻辑)
两种方式:1.render props模式 2.高阶组件(HOC)
注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)

4.2、render props模式

4.2.1、思路分析

思路:将要复用的state和操作state的方法封装到一个组件中
问题一:如何拿到该组件中复用的state?
在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现)
问题2:如何渲染任意的UI?
使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)
(

鼠标当前位置{mouse.x},{mouse.y}

)} />

4.2.2、使用步骤

1 创建Mouse组件,在组件中提供复用的状态逻辑代码(1.状态 2.操作状态的方法)
2 将要复用的状态作为props.render(state)方法的参数,暴露到组件外部
3 使用props.render()的返回值作为要渲染的内容
import React from 'react';
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 创建Mouse组件
class Mouse extends React.Component {
    // 鼠标位置state
    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 (
            

render props模式

{ return (

鼠标位置:{mouse.x},{mouse.y}

) }}>
// Mouse组件的复用 { return (图片) }} />
); } } root.render( )

4.2.3、Mouse组件的复用

Mouse组件负责:封装复用的状态逻辑代码(1.状态 2.操作状态的方法)
状态:鼠标坐标(x,y)
操作状态的方法:鼠标移动事件
传入的render prop负责:使用复用的状态来渲染UI结构

4.2.4、children代替render属性

注意:并不是该模式叫render props就必须使用名为render的prop,实际上可以使用任意名称的prop
把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式
推荐:使用children代替render属性

    {({x,y})=>{

鼠标的位置是:{x},{y}

}
组件内部: this.props.children(this.state)

4.3、高阶组件

4.3.1、概述

目的:实现状态逻辑复用
采用包装(装饰)模式

4.3.2、思路分析

高阶组件(HOC,Higher-OrderComponent)是一个函数,接收要包装的组件,返回增强后的组件
高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给
被包装组件WrappedComponent
const EnhancedComponent = withHOC(WrappedComponent)
// 高阶组件内部创建的类组件
class Mouse extends React.Component{
    render(){
        return 
    }
}

4.3.3、使用步骤

1 创建一个函数,名称约定以with开头
2 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
4 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
5 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
// 创建高阶组件
function withMouse(WrappedComponent){
    // 该组件提供复用的状态逻辑
    class Mouse extends React.Component{}
    return Mouse
}
// Mouse组件的render方法中:
return 
// 创建组件
const MousePosition = withMouse(Position)
// 渲染组件

4.3.4、设置displayName

使用高阶组件存在的问题:得到的两个组件名称相同
原因:默认情况,React使用组件名称作为displayName
解决方式:为高阶组件设置displayName便于调试时区分不同的组件
displayName作用:用于设置调试信息(React Developer Tools信息)
设置displayName:Mouse.displayName=`WithMouse${getDisplayName(WrappedComponent)}`

4.3.5、传递props

问题:props丢失
原因:高阶组件没有往下传递props
解决方式:渲染WrappedComponent时,将state和this.props一起传递给组件
传递方式:

五、React组件进阶总结

1 组件通讯是构建React应用必不可少的一环
2 props的灵活性让组件更加强大
3 状态提升是React组件的采用模式
4 组件生命周期有助于理解组件的运行过程
5 钩子函数让开发者可以在特定的时机执行某些功能
6 render props模式和高阶组件都可以实现组件状态逻辑复用
7 组件极简模型:(state,props) => UI

六、setState()的说明

6.1、更新数据

setState()是异步更新数据的
注意:使用该语法时,后面的setState()不要依赖于前面的setState()
可以多次调用setState(),只会触发一次重新渲染
this.state = {count:1}
this.setState({
    count:this.state.count+1
})
console.log(this.state.count)

6.2、推荐语法

推荐:使用setState((state,props)=>{})语法
参数state:表示最新的state
参数props:表示最新的props
this.setState((state,props)=>{
    return count:state.count + 1
})
console.log(this.state.count)

6.3、第二个参数

场景:在状态更新(页面完成重新渲染)后立即执行某个操作
语法:setState(updater,[,callback])
this.setState(
    (state,props)=>{},
    ()=>{console.log('这个回调函数会在状态更新后立即执行')}
)
this.setState(
    (state,props)=>{},
    ()=>{document.title='更新state后的标题'+this.state.count}
)

七、JSX语法的转换过程

JSX仅仅是createElement()方法的语法糖(简化语法)
JSX语法被@babel/preset-react插件编译为createElement()方法
React元素:是一个对象,用来描述你希望在屏幕上看到的内容

八、组件更新机制

setState()的两个作用:修改state、更新组件(UI)
过程:父组件重新渲染时,也会重新渲染子组件,但只会渲染当前组件子树(当前组件及其所有子组件)

九、组件性能优化

9.1、减轻state

减轻state:只存储跟组件渲染相关的数据
注意:不用做渲染的数据不要放在state中
对于这种需要在多个方法中用到的数据,应该放在this中
class Hello extends Component{
    componentDidMount(){
        // timerId存储到this中,而不是state中
        this.timerId = setInterval(()=>{},2000)
    }
    componentWillUnmount(){
        clearInterval(this.timerId)
    }
    render(){...}
}

9.2、避免不必要的重新渲染

组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
问题:子组件没有任何变化时也会重新渲染
如何避免不必要的重新渲染呢?
解决方式:使用钩子函数shouldComponentUpdate(nextProps,nextState)
作用:通过返回值决定该组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate->render)
class Hello extends Component{
    shouldComponentUpdate(){
        // 根据条件,决定是否重新渲染组件
        return false
    }
    render(){...}
}

9.3、纯组件

纯组件:PureComponent与React.Component功能相似
区别:PureComponent内部自动实现了shouldComponentUpdate钩子,不需要手动比较
原理:纯组件内部通过分别对比前后两次props和state的值,来决定是否重新渲染组件
class Hello extends React.PureComponent{
    render(){
        return (
纯组件
) } } 说明:纯组件内部的对比是shallow compare(浅层对比) 对于值类型来说:比较两个值是否相同(直接赋值即可) let number = 0 let newNumber = number newNumber = 2 console.log(number===newNumber) // false state = {number:0} setState({ number:Math.floor(Math.random()*3) }) // PureComponent内部对比: 最新的state.number===上一次的state.number // false,重新渲染组件 对于引用类型来说:只比较对象的引用(地址)是否相同 const obj={number:0} const newObj=obj newObj,number=2 console.log(newObj===obj) // true state={obj:{number:0}} // 错误做法 state.obj.number=2 setState({obj:state,og}) // PureComponent内部做法: 最新的state.obj===上一次的state.obj // true,不重新渲染组件 注意:state或props中属性值为引用类型,应该创建新数据,不要直接修改原数据,示例: // 正确创建新数据 const newObj={...state.obj,number:2} setState({obj:newObj}) // 正确创建新数据 // 不要用数组的push/unshift等直接修改当前数组的方法 // 而应该用concat或slice等这些返回新数组的方法 this.setState({list:[...this.state.list,{新数据}]})

十、虚拟DOM和Diff算法

10.1、简介

React更新视图的思想是:只要state变化就重新渲染视图
特点:思路非常清晰
问题:组件中有一个DOM元素需要更新时,也得把整个组件的内容重新渲染到页面中?不是
理想状态:部分更新,只更新变化的地方
问题:React是如何做到部分更新的?虚拟DOM配合Diff算法
虚拟DOM:本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容(UI)

10.2、执行过程

1 初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象
2 根据虚拟DOM生成真正的DOM,渲染到页面中
3 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象
4 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
5 最终,React只将变化的内容更新(patch)到DOM中,重新渲染到页面

10.3、代码演示

组件render()调用后,根据状态和JSX结构生成虚拟DOM对象
示例中,只更新p元素的文本节点内容
{
    type:'div',
    props:{
        children:[
            {type:'h1',props:{children:'随机数'}},
            {type:'p',props:{children:0}}
        ]
    }
}
// ...省略其他结构
{type:'p',props:{children:2}}

10.4、React原理揭秘总结

1 工作角度:应用第一,原理第二
2 原理有助于更好地理解React的自身运行机制
3 setState()异步更新数据
4 父组件更新导致子组件更新,纯组件提升性能
5 思路清晰简单为前提,虚拟DOM和Diff保效率
6 虚拟DOM->state+JSX
7 虚拟DOM的真正价值从来都不是性能

你可能感兴趣的:(web前端开发,前端,react.js)