类组件
// 方式一:
class Index extends React.Component {
constructor(props){
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {}
render() {
return <div onClick={this.handleClick}></div>
}
}
// 方式二:
class Index extends React.Component {
handleClick = () => {}
render() {
return <div onClick={this.handleClick}></div>
}
}
class Person {
f1() {}
f2 = () => {}
f3 = function() {}
}
/*
handleClick() {} 最终还是绑定到原型上,只不过 xxx.prototype.handleClick 覆盖了它
但是,箭头函数就不一样了。
sayHello = () => {} : 是绑定在this(实例)上的,和 name = 'a' 一样,都是this的属性;
sayHello() {} : 是原型上的方法,和Index.prototype.sayHello = () => {} 是一样的。
*/
代码中涉及到的关键词理解:
组件类型 | 关键词 | 含义 | 备注 |
---|---|---|---|
类组件 | workInProgress | 当前正在工作的 fiber 对象 | |
ctor | 类组件 | new ctor( props, context ) 实例化组件,得到组件实例 instance | |
函数组件 | current | 当前函数组件对应的 fiber , 初始化 |
|
workInProgress | 前正在工作的 fiber 对象 | ||
Component | 函数组件 | 执行函数组件,得到 return 返回的 React.element对象 | |
props | 函数组件第一个参数 props | ||
secondArg | 函数组件其他参数 | ||
nextRenderExpirationTime | 下次渲染过期时间 |
知识点:
Q:如何区分类组件和函数组件?
答:React 调和渲染 fiber 节点时,依据 fiber tag 来判断。ClassComponent = 1
:类组件;FunctionComponent = 0
:函数组件。
Q:如果没有在 constructor 的 super 函数中传递 props,那么接下来 constructor 执行上下文中就获取不到 props ,这是为什么呢?
/* 假设我们在 constructor 中这么写 */
constructor(){
super()
console.log(this.props) // 打印 undefined 为什么?
}
答:刚才的 Component 源码已经说得明明白白了,绑定 props 是在父类 Component 构造函数中,执行 super 等于执行 Component 函数,此时 props 没有作为第一个参数传给 super() ,在 Component 中就会找不到 props 参数,从而变成 undefined ,在接下来 constructor 代码中打印 props 为 undefined 。
/* 解决方案 */
constructor(props){
super(props)
}
Q:上述绑定了两个 handleClick ,那么点击 div 之后会打印什么呢?
答:结果是 111 。因为在 class 类内部,箭头函数是直接绑定在实例对象上的,而第二个 handleClick 是绑定在 prototype 原型链上的,它们的优先级是:实例对象上方法属性 > 原型链对象上方法属性。
Q:类组件和函数组件区别?
答:类组件,底层只需实例化一次,实例中保存了组件的 state 等状态。对于类组件每一次更新,只需调用 render
函数和生命周期。
函数组件,每一次更新都是一次新的函数执行,一次函数的更新执行,里面的变量会重新声明。
函数组件的状态、副作用怎么保存,React Hooks
来也。
精选评论
- React组件分为类组件和函数组件,本质是承载了渲染视图的 UI 和更新视图的 setState 、 useState 等方法特殊的类和函数。
2. 类组件会创建实例,其实例化过程发生在render阶段updateClassComponent中的constructClassInstance,继承自基类 React.Component,在实例化的时候会设置props、context属性,初始化ref属性为空对象(具体ref绑定发生在commit阶段),而继承了父类 Component 原型上的setState、forceUpdate实际上调用了实例属性updater(updater的装载发生在 adoptClassInstance 里) 对象上的enqueueSetState 和 enqueueForceUpdate 方法。在 mountClassInstance 方法中将state保存到组件实例。
状态更新需要调用 this.setState 或者 this.forceUpdate 进入调度,触发 updateClassInstance 方法,计算新的 state ,返回 sCU的值,在finishClassComponent中根据 sCu 判断是否复用 fiber,不能复用则调用类实例的 render 方法以及对应的生命周期;
NOTE : class内部定义的箭头函数作为实例成员,普通函数作为类成员,实例成员调用优先级高于类成员
3. 函数组件直接调用执行,不会创建实例,原型上的属性方法无效。function 组件执行发生在render阶段中,mout时在 mountIndeterminateComponent分支,update时在updateFunctionComponent分支。在调用 renderWithHooks时,初始化不同 hooksDispatcher,执行不同的hooks,mount时mountWorkInProgressHook创建hook对象,update时updateWorkInProgressHook使用 hook,React通过hooks 记录组件状态,处理副作用。
4. React组件主流通信方式有5种:
- props、callback (最基本的通信方式)
- ref、forwardRef
- context
- 状态管理工具 (redux、mobx)
- eventBus (发布-订阅模式)
5. 增强组件方式
- 类继承
- 自定义 hooks
- 高阶组件 hoc