基础 - Component - 图1

类组件

  1. // 方式一:
  2. class Index extends React.Component {
  3. constructor(props){
  4. super(props)
  5. this.handleClick = this.handleClick.bind(this)
  6. }
  7. handleClick() {}
  8. render() {
  9. return <div onClick={this.handleClick}></div>
  10. }
  11. }
  12. // 方式二:
  13. class Index extends React.Component {
  14. handleClick = () => {}
  15. render() {
  16. return <div onClick={this.handleClick}></div>
  17. }
  18. }
  19. class Person {
  20. f1() {}
  21. f2 = () => {}
  22. f3 = function() {}
  23. }
  24. /*
  25. handleClick() {} 最终还是绑定到原型上,只不过 xxx.prototype.handleClick 覆盖了它
  26. 但是,箭头函数就不一样了。
  27. sayHello = () => {} : 是绑定在this(实例)上的,和 name = 'a' 一样,都是this的属性;
  28. sayHello() {} : 是原型上的方法,和Index.prototype.sayHello = () => {} 是一样的。
  29. */

代码中涉及到的关键词理解:

组件类型 关键词 含义 备注
类组件 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 ,这是为什么呢?

  1. /* 假设我们在 constructor 中这么写 */
  2. constructor(){
  3. super()
  4. console.log(this.props) // 打印 undefined 为什么?
  5. }

答:刚才的 Component 源码已经说得明明白白了,绑定 props 是在父类 Component 构造函数中,执行 super 等于执行 Component 函数,此时 props 没有作为第一个参数传给 super() ,在 Component 中就会找不到 props 参数,从而变成 undefined ,在接下来 constructor 代码中打印 props 为 undefined 。

  1. /* 解决方案 */
  2. constructor(props){
  3. super(props)
  4. }

Q:上述绑定了两个 handleClick ,那么点击 div 之后会打印什么呢?
答:结果是 111 。因为在 class 类内部,箭头函数是直接绑定在实例对象上的,而第二个 handleClick 是绑定在 prototype 原型链上的,它们的优先级是:实例对象上方法属性 > 原型链对象上方法属性。


Q:类组件和函数组件区别?
答:类组件,底层只需实例化一次,实例中保存了组件的 state 等状态。对于类组件每一次更新,只需调用 render函数和生命周期。
函数组件,每一次更新都是一次新的函数执行,一次函数的更新执行,里面的变量会重新声明。
函数组件的状态、副作用怎么保存,React Hooks来也。


精选评论

  1. 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