经过一天周末的休息,和周一忙碌的面试,继续开始react的学习,经过前面的学习(主要是官网),我大概了解react的一些理念和基础的皮毛。为了加深印象,继续学习react组件,看看大佬们怎么做。这里react的版本有了变化 不再是V18了。
组件可以分为两类,一类是类( Class )组件,一类是函数( Function )组件。
类( Class )组件,函数( Function )组件的本质和区别是什么?
/* 类 */
class myClass {
sayHello=()=>console.log('hello, my class')
}
/* 类组件 */
class MyClass extends React.Component{
state={ message:`hello ,Class!` }
sayHello=()=> this.setState({ message : 'hello, my class' })
render(){
return <div style={{ marginTop:'50px' }} onClick={ this.sayHello } > { this.state.message } </div>
}
}
/* 函数 */
function textFun (){
return 'hello, class'
}
/* 函数组件 */
function FunComponent(){
const [ message , setMessage ] = useState('hello,class')
return <div onClick={ ()=> setMessage('hello, my class') } >{ message }</div>
}
这里我觉得只是写法不同,两种组件承载了渲染视图的 UI 和更新视图的 setState 、 useState 等方法,但是我们需要了解在 React 调和渲染 fiber 节点的时候,如果发现 fiber tag 是 ClassComponent = 1,则按照类组件逻辑处理,如果是 FunctionComponent = 0 则按照函数组件逻辑处理。
class类组件
那么到底什么是类组件呢?
类组件的定义
在 class 组件中,除了继承 React.Component ,底层还加入了 updater 对象,组件中调用的 setState 和 forceUpdate 本质上是调用了 updater 对象上的 enqueueSetState 和 enqueueForceUpdate 方法。
React 处理组件的逻辑
Component 底层 React 的处理逻辑是,类组件执行构造函数过程中会在实例上绑定 props 和 context ,初始化置空 refs 属性,原型链上绑定setState、forceUpdate 方法。对于 updater,React 在实例化类组件之后会单独绑定 update 对象。
类组件的构成
class Index extends React.Component{
constructor(...arg){
super(...arg) /* 执行 react 底层 Component 函数 */
}
state = {} /* state */
static number = 1 /* 内置静态属性 */
handleClick= () => console.log(111) /* 方法: 箭头函数方法直接绑定在this实例上 */
componentDidMount(){ /* 生命周期 */
console.log(Index.number,Index.number1) // 打印 1 , 2
}
render(){ /* 渲染函数 */
return <div style={{ marginTop:'50px' }} onClick={ this.handerClick } >hello,React!</div>
}
}
Index.number1 = 2 /* 外置静态属性 */
Index.prototype.handleClick = ()=> console.log(222) /* 方法: 绑定在 Index 原型链的 方法*/
函数组件
ReactV16.8 hooks 问世以来,对函数组件的功能加以强化,可以在 function 组件中,做类组件一切能做的事情,甚至完全取缔类组件。
import { useState } from 'react';
export default function Gallery() {
const [index, setIndex] = useState(0); /* hooks */
function handleClick() {
setIndex(index + 1);
}
return (
<>
// onClick={handleClick}
<>
)
}
对于类组件来说,底层只需要实例化一次,实例中保存了组件的 state 等状态。对于每一次更新只需要调用 render 方法以及对应的生命周期就可以了。但是在函数组件中,每一次更新都是一次新的函数执行,一次函数组件的更新,里面的变量会重新声明。
组件的通讯方式
React 一共有 5 种主流的通信方式:
-
props 和 callback 方式(vue props和回调)
-
ref 方式。(vue3 ref调用属性和事件)
-
React-redux 或 React-mobx 状态管理方式。(Pinia,vueX)
-
context 上下文方式。Vue3 中 Context的用法
// Attribute (非响应式对象) console.log(context.attrs) // 插槽 (非响应式对象) console.log(context.slots) // 触发事件 (方法) console.log(context.emit) -
event bus 事件总线。(vue bus总线,需要手动挂载取消,复杂业务状态有位置影响,都违背了数据单象传递)
props 和 callback
上文提过,属性传递,可以传 整个props也可以把属性分开传递,这里的传递类似于vue的props:
父组件 -> 通过自身 state 改变,重新渲染,传递 props -> 通知子组件
子组件 -> 通过调用父组件 props 方法 -> 通知父组件。
实现父子组件之间的通信,这里拿。
/* 子组件 */
function Son(props){
const { fatherSay , sayFather } = props
return <div className='son' >
子组件
<div> 父组改变props:{ fatherSay } </div>
<input placeholder="回调父组件说" onChange={ (e)=>sayFather(e.target.value) } />
</div>
}
/* 父组件 */
function Father(){
const [ childSay , setChildSay ] = useState('')
const [ fatherSay , setFatherSay ] = useState('')
return <div className="box father" >
父组件
<div> 子组件回调展示:{ childSay } </div>
<input placeholder="修改props传递的内容" onChange={ (e)=>setFatherSay(e.target.value) } />
<Son fatherSay={fatherSay} sayFather={ setChildSay } />
</div>
}
最后关于类组件的继承,这里我的设想是 使用ts感觉更好一些,后续来实践。