React(zf9-1)

一、概述

基础知识、核心原理、项目实战

npm i create-react-app -g

create-react-app 项目名称

// 查看版本
create-react-app --version

开发效率

二、JSX

vscode如何支持jsx语法:将文件后缀名改成jsx即可

在ReactDOM.createRoot()的时候,不能直接把html/body作为根容器

1.number/string:值是啥渲染出来就是啥
2.boolean/null/undefined/Symbol/BigInt:渲染的内容是空
3.数组:把数组的每一项都分别拿出来渲染,并不是变为字符串渲染(中间没有逗号)
4.除数组外,其余对象一般都不支持(特殊情况:JSX虚拟dom对象、给元素设置style行内样式)

React.createElement(ele, props, ...children)
1.ele:标签名或组件名
2.props:属性集合(对象),如果没有设置任何属性则值为null
3.children:第三个及以后的参数都是当前元素的子节点

createElement方法执行创建出虚拟dom对象
render:把虚拟dom变为真实dom

for...in:既可以迭代私有的,也可以迭代公有的,只能迭代可枚举、非Symbol类型的属性
Object.getOwnPropertyNames():获取对象非Symbol类型的私有属性(无关是否可枚举)
Object.getOwnPropertySymbols():获取对象Symbol类型的私有属性
Reflect.ownKeys():获取所有的私有属性

当type是一个函数时:
1.把函数执行
2.把虚拟dom中的props作为实参传递给函数
3.接收函数执行的返回结果(即当前组件的虚拟dom对象)
4.基于render把组件返回的虚拟dom变为真实dom

三、组件

1.props

关于对象的规则设置:
Object.freeze(obj):冻结对象(被冻结的对象不能修改、新增、删除成员,不能给成员做劫持)
Object.isFrozen(obj):检测是否被冻结,返回true或false
Object.seal(obj):密封对象(被密封的对象可以修改,但不能新增、删除成员,不能给成员做劫持)
Object.isSealed(obj):检测是否被密封
Object.preventExtensions(obj):把对象设置为不可扩展(除了不能新增成员,其余的操作都可以)
Object.isExtensible(obj):检测是否可扩展

// 设置默认值(函数组件)
MyComponent.defaultProps = {
    count: 0
}


import PropTypes from 'prop-types'
// 设置其他规则(数据值格式、是否必传...)
MyComponent.propTypes = {
    title: PropTypes.string.isRequired
}

2.class

class Person {
    // new的时候执行的构造函数
    // 需要接收传递进来的实参才需要设置constructor
    constructor(x, y) {
        // this:创建的实例
        this.total = x + y
    }
    num = 1 // 等价于this.num = 1 给实例设置私有属性
    // 私有的方法
    getNum1 = function () { }
    // 私有的方法
    getNum2 = () => {
        console.log(this) // this:当前创建的实例
    }
    // 公共的方法(原型上的)
    getCount() { }
    // 把构造函数当做一个普通对象,设置静态的私有属性方法
    static sum = 100
    // 把构造函数当做一个普通对象,设置静态的私有属性方法
    static getSum() { }
}
// 设置公共的属性
Person.prototype.count = 1

// 基于extends实现继承
// 1.首先基于call继承:React.Component.call(this) // this:Person类的实例p
// 2.再基于原型继承:Person.prototype.__proto__ === React.Component.prototype
// 实例->Person.prototype->React.Component.prototype->Object.prototype
class Person extends React.Component {
    constructor() {
        // 只要设置了constructor,则内部第一句话一定要执行super()
        // 如果没有设置constructor,则内部默认加了constructor() {super()}
        super()
    }
}

let p = new Person()

3.类组件

import PropTypes from 'prop-types'

class MyComponent extends React.Component {
    // 属性规则校验
    static defaultProps = {
        count: 0
    }
    // 其他规则
    static propTypes = {}
    constructor() {
        super()
    }
}

// 在constructor处理完毕之后,react内部会把传递的props挂载到实例上,所以在其他的函数中只要保证this是实例,就可以基于this.props获取传递的属性

this.forceUpdate():强制更新(会跳过shouldComponentUpdate函数的校验)

4.PureComponent

Object.is(NaN, NaN)是相等的

PureComponent会给类组件默认加一个shouldComponentUpdate,在函数中它会对新旧属性和状态做一个浅比较(只比较第一层),如果没有改变则返回false

5.ref

class MyComponent extends React.Component {
    box3 = React.createRef()
    componentDidMount() {
        console.log(this.refs.box1)
        console.log(this.box2)
        console.log(this.box3.current)
    }
    render() {
        return (
            <div>
                <div ref="box1">1</div>
                {/* x是函数的形参,存储的是当前的dom元素 */}
                <div ref={(x) => this.box2 = x}>2</div>
                <div ref={this.box3}>3</div>
            </div>
        )
    }
}

给标签设置ref:获取dom元素
给类组件设置ref:获取组件的实例
给函数组件设置ref直接报错,但是可以配合React.forwardRef实现ref的转发(获取函数组件(子组件)内部的某个元素)

const Child = React.forwardRef(function Child(props, ref) {
    return (
        <button ref={ref}>按钮</button>
    )
})
class Parent extends React.Component {
    componentDidMount() {
        console.log(this.button) // 子组件内部的button按钮
    }
    render() {
        return (
            <Child ref={x => this.button = x} />
        )
    }
}

6.setState

this.setState(partialState, callback)
partialState:支持部分状态更改
callback:在状态更改、视图更新完毕后触发执行(只要执行了setState,callback一定会执行)
1.发生在componentDidUpdate函数之后,componentDidUpdate会在任何状态更改后都触发执行,而回调函数可以在指定状态更新后处理一些事情
2.基于shouldComponentUpdate阻止了状态/视图更新,componentDidUpdate不会执行,但callback回调函数依然会触发执行

在react18中,setState在任何地方执行都是异步的(实现状态的批处理(统一处理))

在当前相同的时间段内,遇到setState会立即放入到更新队列中

flushSync:可以刷新updater更新队列,即让修改状态的任务立即批处理一次(在flushSync操作结束之后会立即刷新更新队列)
import {flushSync} from 'react-dom'
flushSync(() => {
    this.setState({})
})
或
flushSync()

// prevState:存储之前的状态值
this.setState(prevState => {
    // return的对象就是我们想要修改的新状态值
    return {}
})

7.合成事件

class MyComponent extends React.Component {
    handleClick1() {
        console.log(this) // undefined
    }
    // 只要方法经过bind处理了,那么最后一个实参就是传递的合成事件对象
    handleClick2(x, y, e) {
        console.log(this, x, y) // 实例、1、2
        console.log(e)
    }
    handleClick3 = (e) => {
        console.log(this) // 实例
        console.log(e)
    }
    render() {
        return (
            <div>
                <button onClick={this.handleClick1}>button1</button>
                <button onClick={this.handleClick2.bind(this, 1, 2)}>button2</button>
                <button onClick={this.handleClick3}>button3</button>
            </div>
        )
    }
}

合成事件对象中的nativeEvent:可以获取浏览器内置的原生事件对象

window、document、html、body

事件传播机制
捕获阶段:从最外层向最里层逐一查找
目标阶段:事件源
冒泡阶段:从里到外

事件是浏览器赋予元素的默认行为

event.stopPropagation():阻止事件的传播(包含捕获和冒泡)
event.stopImmediatePropagation:阻止事件的传播(当前元素绑定的其他方法,如果还未执行,也不会再执行了(同一级的方法))

event.target:事件源(点击的是谁,谁就是事件源)

事件委托的好处:提高JS代码运行的性能、给动态绑定的元素做事件绑定
限制:当前操作的事件必须支持冒泡传播机制,例如mouseenter、mouseleave等事件是没有冒泡传播机制的

<div onClick={() => {
    console.log('合成 冒泡')
}} onClickCapture={() => {
    console.log('合成 捕获')
}}></div>

react中合成事件的处理原理:不是给当前元素单独的事件绑定,是基于事件委托处理的
1.在react17以前,都是委托给document容器的,而且只做了冒泡阶段的委托
2.在react17及以后,都是委托给#root这个容器的,捕获和冒泡都做了委托

event.path // path:所有祖先元素(事件源->window)

@珠(zxt)[23]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容