ZF_react ref的实现 生命周期的实现

ref的实现

类组件可以通过ref来获取原生dom,获取类组件,并且可以通过forwardRef转发函数组件来传递ref。

原生dom ref的实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实现收集第一二个输入框的值相加并且展现在第三个输入框上。
首先先实现简单的React.createRef函数在这里插入图片描述
该函数很简单,返回一个拥有current属性的对象,重点是赋值,因为拿到的是原生dom,所以肯定是在创建的时候赋值。
在这里插入图片描述
在创建vdom的时候先对ref做处理,因为是特殊属性。接着,原生dom只需要在创建的时候赋值即可。在createDom函数里面赋值:
在这里插入图片描述
这样就完成原生dom的ref处理。看效果:
在这里插入图片描述

类组件的ref实现

在这里插入图片描述
在这里插入图片描述
实现点击外部组件调用类实例的getFocus方法去让类组件的input获取焦点。
类组件的ref是整个类实例,也就是vdom,而不是真实的dom,所以相对的处理应该在类组件实例创建的时候,也就是mountClassComponent函数执行的时候
在这里插入图片描述
在new的时候获取实例,并且直接赋值给ref,就可以通过ref.current获取到该实例,自然也能调用该实例上的方法,效果:
在这里插入图片描述
此时的ref的Current指向的是类实例(vdom)

函数组件ref的实现

接着实现函数组件的ref。
函数组件是不可以直接接受ref的,因为其没有实例,调用完就释放.
在这里插入图片描述

在这里插入图片描述
所以我们必须通过转发的形式,实现React.forwardRef函数,完成一点击伏组件就获取函数组件里面的Input焦点功能。

  • React.forwardRef的实现,因为只有类组件可以接受ref,所以如图:在这里插入图片描述
    通过forwardref包裹,返回一个新的类组件,通过该类组件的props将ref作为第二个参数传入。所以函数组件就可以拿到外部传进来的ref
    在这里插入图片描述
    这时候的ref.current指向的是内部组件的input,所以直接调用focus方法即可。
    在这里插入图片描述
    在这里插入图片描述
    效果一样。
    以上就是通过包装类组件使函数组件拿到ref。但是实际上源码不是这样处理的。

通过一种特殊的类型来区分。

在这里插入图片描述
首先还是对props中的ref删掉。然后
在这里插入图片描述
定义一个类型,源码中,React.forwardRef返回是一个对象,类似于

	{ $$typeof: Symbol('react.forward_ref'), render: f(){} // 函数组件的函数}

所以我们也返回这个东东,
在这里插入图片描述
然后在创建dom的时候需要特别注意区分这个类型,
在这里插入图片描述
在这里插入图片描述
通过vdom拿到props和ref,然后执行函数组件,传入props和ref,再返回一个vdom,通过createDom创建一个真实dom。这样就完成了函数组件ref的转发/

总结:

react的类组件可以通过createRef获取一个对象来获取dom,类组件实例,或者函数组件的ref,原生dom是直接获取真实dom,而类组件的ref是获取类组件的实例,也就是vdom,函数组件不接受ref,,必须通过forwardRef进行包裹,返回一个新的类组件,讲ref作为第二个参数传入函数组件。真正的react源码的实现是通过一种特殊的类型进行区分,在创建dom的时候特别处理了forwardRef返回的vdom。传入对应的ref,通过这样来转发ref。

老的生命周期的实现

在这里插入图片描述

创建过程的生命周期

在这里插入图片描述
在这里插入图片描述
正确的顺序应该是1234。接着看实现
因为willMount和DidMount是在render执行前后调用的,我们找到render执行的地方
在这里插入图片描述

在创建类组件的时候,render执行之前调用componentWillMount,在render执行过后调用componentDidMount。构造函数是在new的时候执行的,看效果:
在这里插入图片描述
首次更新,顺序正确。

扫描二维码关注公众号,回复: 13200966 查看本文章

接着修改的生命周期

在这里插入图片描述
主要实现这三个。shouldComponentUpdate是用来当State和Props变化的时候判断当前组件是否更新,返回一个布尔值。
所以他执行的地方应该是this.setState之后获取到新的state在那个时候做判断(props稍后再说)
在这里插入图片描述
在shouldUpdate这个方法做对应的操作,因为这个方法接受的就是最新的props和最新的state。在这里判断是否需要更新。需要的话就必须执行componentWillUpdate函数。然后在更新完毕后就必须调用componentDidUpdate函数了
在这里插入图片描述
看看效果:
在这里插入图片描述
number = 1,不符合number % 2 === 0的结果,所以不再向下进行。
第二次:
在这里插入图片描述
state更新后继续执行shouldComponnetUpdate函数,符合条件,必须更新,就会走对应的神功周期,可以看到。顺序执行正确!

componentWillReceiveProps和componentWillUmount的实现

这块涉及到子组件的渲染,所以我们得先完成dom diff算法。
之前为了先学习简单使用dom.replaceChild去替换掉两个dom。现在重新实现一下compareTwoVdom

compareTwoVdom的实现

首先还是先判断简单的。

  • 新老vdom都没有,直接返回
  • 新的dom为null,老的不为Null。直接销毁,顺便调用销毁函数。
  • 老的为null,新的不为null,重新创建,顺便调用didMonut函数。
    在这里插入图片描述
  • 老的有,新的也有,但是元素不同,直接替换,顺便执行对应的生命周期函数。componentWillUnmount就是在这里执行的
    在这里插入图片描述
  • 然后就是最难的,两个都有而且类型相同
    在这里插入图片描述
    调用updateElement函数。
    在这里插入图片描述
  • 文本节点比较好处理,原始节点需要实现两个函数,updateProps之前实现过了,用来替换props。
    在这里插入图片描述
    然后再实现updateChildren函数。
    先简单实现
    在这里插入图片描述
    直接从头替换,简单实现。
  • 接着就是新老vdom的type都是组件的类型。
    在这里插入图片描述
    主要实现两个函数。先看updateClassComponent。
    思路就是:调用之前实现的每个类组件的setState方法,会调用updater的addState到emitUpdate方法。
    在这里插入图片描述
    在这里插入图片描述
    我这里类组件的更新的时候,它是通过compareTwoVdom进来的,如:
    在这里插入图片描述
    每次执行更改的时候,是通过比对内层的vdom,此时内层的vdom一般都是div为type,如
    在这里插入图片描述
    当其遍历到Son这个组件的时候,就判定是类组件。所以在这里,这些oldVdom指的是,Son这个vdom,也就是type为function Son(){}等等一些啥的

在这里插入图片描述
这个类实例拿到classInstance的是Son的内层vdom,也就是type为div啥的。然偶将其挂载到新的vdom上去。
这个Son一般是通过props改变而需要改变。所以还需要调用在这里插入图片描述
componentWillReceiveProps方法,就是在这里执行的。外层组件的props的改变导致内层组件需要更改,就调用该函数。然后需要调用该实例的emitUpdate方法,将最新的props传进去。调用了emitUpdate方法后,会执行一系列流程,如调用shouldComponentUpdate
在这里插入图片描述
然后再去执行类实例的forceUpdate方法在这里插入图片描述
调用了该方法之后,oldRenderVdom拿到的是内层的vdom,也就是类组件的在这里插入图片描述
这个render返回的vdom。然后就要计算新的Son组建的vdom,再执行一遍render方法。然后这两个进行compareTwoVdom,又进去判断,此时两个没有类组件啥的,直接compare对比就可以做出更改。
在这里插入图片描述
我们之前实现了updateChildren方法,他就是进行diff的关键,我们会发现不管是什么组件,到最后的vdom都是type为原始dom的,如div这些,所以就会执行updateChildren这个方法去进行diff算法,我们现在只是简单实现了一下。
在这里插入图片描述
这里有一步操作就是执行nextVdom寻找的时候,在这里插入图片描述
当第四的时候组件卸载,第六的时候组件重新挂载,这时候就需要插入到对应的位置了。所以通过NextVdom永远拿到弟弟。因为即使是null,也会站vdom的一位。在这里插入图片描述
所以他的弟弟就是函数组件,直接在这里插入图片描述
oldVdom为null,在这里插入图片描述
新的dom为Son,直接创建dom并且挂载到函数组件前面即可。
这样生命周期就简单实现完成了。

总结

组件的更新的时候可能会有点懵,因为类组件和函数组件的vdom是跟他们return或者render的返回的vdom是不同的,一般我们都是通过比对新老内层vdom来进行diff算法。

  • 当内层vdom也有类组件的时候,他的改变就是通过外层的props改变,就需要重新执行该类组件的emitUpdate方法,并且执行ComponentWIllReceiveProps方法。emitUpdate方法之后会做一系列操作,最终拿到子类组件的新老内层vdom然后继续去执行compareTwoVdom方法。
  • 而内层有函数组件就更容易了,直接调用获取返回的vdom,直接进行新老vdom的比对就可以了。
  • 对应的生命周期分别会在对应的时间点执行。
    gitee 源码:https://gitee.com/fine509/react_origin_code_by_15

猜你喜欢

转载自blog.csdn.net/lin_fightin/article/details/120661693
今日推荐