React源码解析18(5)------ 实现函数组件【修改beginWork和completeWork】

摘要

经过之前的几篇文章,我们实现了基本的jsx,在页面渲染的过程。但是如果是通过函数组件写出来的组件,还是不能渲染到页面上的。

所以这一篇,主要是对之前写得方法进行修改,从而能够显示函数组件,所以现在我们在index.js文件中,修改一下jsx的写法。修改成函数组件:

javascript 复制代码
import jsx from '../src/react/jsx.js'
import ReactDOM from '../src/react-dom/index'

const root = document.querySelector('#root');

function App() {
  return jsx("div", {
    ref: "123",
    children: jsx("span", {
      children: "456"
    })
  });
}

ReactDOM.createRoot(root).render(<App />)

这里因为需要使用我们自己的jsx方法。所以在App里面返回的依旧是通过之前的方式进行调用。

1.修改reconcileChildren方法

我们来回忆一下,在beginWork阶段,我们主要是通过ReactElement,创建FilberNode。而reconcileChildren,就是创建FilberNode的方法。

在之前我们只处理了HostText类型和HostComponent类型,所以在这个方法里面,我们要对函数类型进行兼容,而作为函数组件的ReactElment,它最显而易见的特点就是type的值是一个函数。

例如上面的App组件,对应的ReactElement的type就是App。所以我们可以通过type来判断组件的类型:

javascript 复制代码
function reconcileChildren(element) {
  let tag;
  if(typeof element.type === 'function') {
    tag = FunctionComponent
  }
  //其他代码
  console.log(filberNode)
  return filberNode
}

我们打印一下看看,这个函数组件是否满足预期:

2.updateFunctionComponent方法

现在有了tag为FunctionComponent类型的FilberNode,在beginWork里面,我们就要对这个类型的FilberNode进行处理:

javascript 复制代码
function beginWork(nowFilberNode) {
  switch (nowFilberNode.tag) {
  	//其他代码
    case FunctionComponent: {
      return updateFunctionComponent(nowFilberNode)
    }
    //其他代码
  }
}

现在我们来实现updateFunctionComponent方法。

之前对于HostComponent类型的FilberNode,它的子节点其实就是它对应的ReactElement。

但是对于函数类型的FilberNode,我们想一下不就是它自己的返回值嘛?所以我们直接调用这个函数就能拿到它的子FilberNode了。

javascript 复制代码
function updateFunctionComponent(filberNode) {
  const nextChildren = filberNode.type();
  const newFilberNode = reconcileChildren(nextChildren);
  filberNode.child = newFilberNode;
  newFilberNode.return = filberNode;
  beginWork(newFilberNode)
}

2.修改completeWork方法

对于completeWork方法, 它的主要作用(目前)是给对应的FilberNode增加stateNode,而函数组件并没有自己对应的StateNode,所以直接继续递归就可以了:

javascript 复制代码
export const completeWork = (filberNode) => {
  const tag = filberNode.tag
  switch (tag) {
	//其他代码。。。
    case FunctionComponent: {
      completeWork(filberNode.child)
    }
  }
}

3.修改commitWork方法

对于之前的commitWork,我们是直接将最外层的FilberNode的stateNode挂载了容器上,现在由于最外层的可能是FunctionComponent,它是没有自己的stateNode的。所以我们要找到具有stateNode的最外层FilberNode。

javascript 复制代码
import { HostComponent } from "./filberNode";

export function commitWork(filberRootNode) {
  const container = filberRootNode.container;
  let node = filberRootNode.finishedWork;
  while( node.tag !== HostComponent ){
    node = node.child
  }
  container.appendChild(node.stateNode)
}

OK,经过上面的修改,我们的App组件也可以正常渲染了。

相关推荐
Monly2130 分钟前
Vue:下拉框多选影响行高
前端·javascript·vue.js
小桥风满袖38 分钟前
极简三分钟ES6 - ES8中对象扩展
前端·javascript
超人不会飛1 小时前
Vue markdown组件 | 流式 | 大模型应用
前端·javascript·github
艾小码2 小时前
Vue模板进阶:这些隐藏技巧让你的开发效率翻倍!
前端·javascript·vue.js
艾小码2 小时前
还在手动加载全部组件?这招让Vue应用性能飙升200%!
前端·javascript·vue.js
三十_2 小时前
【实录】多 SDK 日志乱象的解决方案:统一日志 SDK 设计分享
前端·javascript
Samsong2 小时前
JavaScript逆向之对称加密算法
javascript·逆向
一枚前端小能手2 小时前
🛡️ Token莫名其妙就泄露了?JWT安全陷阱防不胜防
前端·javascript·安全
李游Leo2 小时前
JavaScript事件机制与性能优化:防抖 / 节流 / 事件委托 / Passive Event Listeners 全解析
开发语言·javascript·性能优化
复苏季风2 小时前
Vue3 小白的疑惑:为什么用 const 定义的变量还能改?
前端·javascript·vue.js