一、概述
基础知识、核心原理、项目实战
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]