js中 class类
class Parent {
// new 的时候,执行的构造函数(可写可不写:需要接收传进来的实参信息,才需要constructor)
constructor(x,y){
// this -> 创建的实例
this.total = x + y
}
num = 2000 // 等价于 this.num=2000 给实例设置私有属性
getNum = ()=>{
// 箭头函数没有自己的this,所用的this是宿主环境中的
console.log(this) // this -> 当前创建的实例
}
sum(){
// 类似于 sum = function sum(){}
// 它是给Parent.prototype上设置公共方法 [sum是不可枚举的]
}
// 把构造函数当作一个普通对象,为其设置静态的私有属性方法 Parent.xxx
static avg = 1000;
static average(){
}
}
// 在外部手动给构造函数原型上设置公共的属性
Parent.prototype.y = 2000
let p = new Parent(10,20)
console.log(p)
console.dir(Parent)
React中的组件
函数组件是静态组件
- 组件第一次渲染完毕后,无法基于内部的某些操作 让组件更新【无法实现自更新】,但是,如果调用它的父组件更新了,那么调用它的子组件一定会更新【可能传递最新的属性值来】
- 函数组件具备:属性 【其他状态等都没有】
- 优势:比类组件处理机制简单,导致函数组件渲染速度更快
类组件是动态组件:
- 组件在第一次渲染完毕后,除了父组件更新可以触发其更新外,还可以通过:this.setState 修改状态,或者this.forceUpdate 等方式让组件实现【自更新】
- 类组件具备:属性、状态、周期函数、ref … 【几乎组件应该有的都具备】
- 优势:功能强大
Hooks组件:【推荐】
- 具备了函数组件和类组件各自的优势,在函数组件的基础上,基于hooks函数,让函数组件也可以拥有状态、周期函数等,让函数组件也可以实现自更新【动态化】
React中的类组件
index.jsx 主页面
import React from 'react'
import ReactDOM from 'react-dom/client'
import Vote from './Vote'
const root = ReactDOM.creatRoot(document.getElementById('root'))
root.render(
<>
<Vote title="学习React" />
</>
)
render 函数在渲染的时候,如果type 是:
字符串:创建一个标签
普通函数:把函数执行,并把props传递给函数
构造函数:把贵族凹函数基于new执行【也就是创建类的一个实例】,也会把解析出来的props传递过去
没第哦啊用一次类组件都会创建一个单独的实例
把在类组件中编写的render函数执行,把返回的jsx 【virtualDom】当作组件视图进行渲染
例如:new Vote({ title:’学习React’ })
Vote.jsx 组件
创建类组件
0、创建一个构造函数(类)
- 要求必须即继承React.Component / PureComponent 这个类
- 我们习惯于使用ES6中的class创建类 [ 因为方便 ]
- 必须给当前类设置一个render的方法 [ 放在其原型上 ] : 在render方法中,返回需要渲染的视图
从调用类组件 [ new Vote({…}) ] 开始,类组件内部发生的事情
初始化属性 && 规则校验
先进行规则校验,校验完毕后,再处理属性的其他操作
方案一
constructor(props){
super(props) // 会把传递过来的属性挂载到this实例上
console.log(this.props) // 获取到传递的属性
}
方案二
即使不在constructor中处理【或者constructor都没写】,在constructor处理完毕后,React内部也会把传递的props挂载到实例上;所以在其他的函数中,只要保证this是实例,就可以基于this.props获取传递的属性
- 同样this.props获取的属性对象也是被冻结的【只读】,Object.isFrozen(this.props) -> true
初始化状态
状态:后期修改状态,可以触发视图的更新
需要手动初始化,如果没有进行相关的处理,则默认会往实力上挂载一个state,初始值是null,即this.tate=null
手动处理:state = { … }
修改状态,控制视图更新
this.state.xxx = xxx ;这种操作只改变了状态值,但是无法让视图更新
要想让视图更新,需要基于React.Component.prototype提供的方法进行操作
this.setState(partialState) 既可以修改状态,也可以让视图更新 【推荐】
partialState 部分状态
this.setState({ xxx:xxx,})
this.forceUpdate() 强制更新
触发 componentWillMount 周期函数 :在组件第一次渲染之前
钩子函数 :程序运行到某个阶段,React可以提供一个处理函数,让开发者在这个阶段做一些自定义的事情
componentWillMount 触发 在组件第一次渲染之前
目前是不安全的【虽然可以用,但是未来可能要被移除,不建议使用】
使用时会抛出黄色警告,可以暂时使用 UNSAFE_componentWillMount 周期函数
如果开启了React.strictMode【React的严格模式】,则 使用 UNSAFE_componentWillMount 会抛出红色警告错误
React.strictMode :React的严格模式,会检查React中的一些不规范的语法,或者是不建议使用的API等
”use strict“ js的严格模式
触发render 周期函数:渲染
触发componentDidMount 周期函数 :第一次渲染完毕
- 已经把 virtualDom 渲染成真是DOM 【意味着可以获取真是dom了】
组件更新的逻辑 【当改变了相关状态,组件会更新】
- 触发 shouldComponentUpdate 周期函数:返回 true / false是否允许更新
- 触发 componentWillUpdate 周期函数:更新之前
- 此周期函数也是不安全的
- 在这个阶段状态还没有改变
- 修改状态值 / 属性值 【让this.state.xxx成为最新的值】
- 触发 render 周期函数:组件更新
- 按照最新的状态 / 属性,把JSX编译为virtualDom
- 和上次渲染出来的virtualDom进行对比 【DOM-DIFF】
- 把差异的部分进行渲染【渲染真实的dom】
- 触发 componentDidUpdate 周期函数:组件更新完毕
【特殊说明:如果是基于this.forceUpdate()强制更新视图,会跳过shouldComponentUpdate周期函数的校验,直接从willUpdate开始进行更新,也就是说视图一定会进行更新】
父子组件嵌套,处理机制上遵循深度优先原则:父组件在操作过程中,遇到子组件,一定是把子组件处理完,父组件才能继续处理
父组件第一次渲染
父 willMount -> 父render 【子 willMount-> 子render-> 子didMount 】-> 父didMount
父组件更新
父 shouldUpdate-> 父willUpdate-> 父render -> 【子 willReceiveProps -> 子shouldUpdate -> 子willUpdate -> 子render ->子didUpdate】 -> 父didUpdate
组件销毁的逻辑
- 触发componentWillUnmount 周期函数:组件销毁之前
- 销毁
import React from "react"
import PropTypes from 'prop-types'
class Vote extends React.Component{
// 规则属性校验
static defaultProps = {
num:0
}
static propTypes = {
title:PropTypes.string.isRequired,
num:PropTypes.number
}
constructor(props){
super(props)
console.log(this.props)
}
state = {
supNum:20,
oppNum:10
}
render(){
let {title} = this.props,
{supNum,oppNum} = this.state
return (
<div className="vote-box">
<div className="header">
<h2>{title}</h2>
<span>{supNum + oppNum}</span>
</div>
<div className="main">
<p>支持人数:{supNum}</p>
<p>反对人数:{oppNum}</p>
</div>
<div className="footer">
<button onClick={()=>{
this.setState({
supNum:supNum+1
})
}}>支持</button>
<button onClick={()=>{
this.forceUpdate()
}}>反对</button>
</div>
</div>
)
}
componentWillMount(){ // UNSAFE_
console.log('componentWillMount:第一次渲染之前')
}
componentDidMount(){
console.log('componentDidMount:第一次渲染完成')
}
shouldComponentUpdate(nextProps,nextState){
// nextState:存储要修改的最新状态
// this.state:存储的还是修改前的状态【此时状态还没有改变】
console.log(this.state,nextState)
// 此周期函数需要返回 true / false
// true:允许更新,会执行下一个操作
// false:不允许更新,接下来啥都不处理,阻断操作
return true
}
componentWillUpdate(){ // UNSAFE_
console.log('componentWillUpdate:组件更新之前')
}
componentDidUpdate(){
console.log('componentDidUpdate:组件更新完成')
}
componentWillReceiveProps(nextProps){ // UNSAFE_
console.log('componentWillReceiveProps:接收最新属性之前',this.props,nextProps)
}
componentWillUnmount(){
console.log('componentWillUnmount:组件销毁之前')
}
}
export default Vote
Demo.jsx
PureComponent 与 Component 的区别
PureComponent 会给类组件加一个shouldComponentUpdate【内置,代码加会报错】 周期函数
- 在次周期函数内 它对新老属性、状态进行浅比较
- 如果经过浅比较,发现属性和状态没有改变,返回false 【也就是不会更新组件】,返回true【更新组件】
class Demo extends React.PureComponent{
state = {
arr : [10,20,30]
}
render (
<div>
{
arr.map((item,index)=>{
return <span key={index}>{item}</span>
})
}
<br />
<button onClick={
()=>{
arr.push(40)
this.setState({
arr // 最新修改的状态地址不变 PureComponent 浅比较下 组件不更新
})
}
}></button>
</div>
)
// PureComponent 内置钩子,代码加会报错
shouldComponentUpdate(nextProps,nextState){
let {props,state} = this
// props / state : 修改之前的属性状态
// nextProps / nextState : 将要修改的属性状态
return !shallowEqual(props,nextProps) || !shallowEqual(state,nextState)
}
}
浅比较
浅比较只比较对象的第一级,对于深层次的内容不会继续宁比较
先比较对象成员的数量,如果不一致,则两个对象肯定不一致
判断属性key是否两个对象都存在,另一方不存在则对象肯定不一致
对比两个属性的值是否一致
【注意:直接对比 NaN === NaN 为false ,Object.is(NaN,NaN) 为true】
// 检测是否为对象 且 不为null
const isObject = function isObject(obj){
return obj !== null && /^(object|function)$/.test(typeof obj)
}
// 对象浅比较的方法
const shallowEqual = function shallowEqual(objA,objB){
// 有一方不是对象 返回false
if(!isObject(objA) || !isObject(objB)) return false
// 两方对象引用地址一致 返回true
if( objA === objB ) return true
let keysA = Reflect.ownKeys(objA),
keysB = Reflect.ownKeys(objB);
// 属性数量不一致 返回false
if(keysA.length !== keysB.length) return false
// 数量一致,比较内部成员【只比较第一级】 for可以随时退出循环
for(let i=0;i<keysA.length;i++){
let key = keysA[i]
// objB不存在objA的属性 或 属性都存在但是值不相等 返回false
if(!objB.hasOwnProperty(key) || !Object.is(objA[key],objB[key]) ) return false
}
// 以上处理完成,没有发现不相同的成员,则认为两个对象是相等的【浅比较】
return true
}
let obj = { z : 20 } // 0X001
let objA = {
X:10,
Y:obj, // 0X001
ARR:[10,20,30] // 0X002
}
OBJ.N = 999
let objb = {
X:10,
Y:obj, // 0X001
ARR:[10,20,30] // 0X003
}
// 改变属性进行对象比较
console.log(shallowEqual(objA,objB))