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组件也可以正常渲染了。

相关推荐
速易达网络36 分钟前
RuoYi、Vue CLI 和 uni-app 结合构建跨端全家桶方案
javascript·vue.js·低代码
耶啵奶膘42 分钟前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
JoJo_Way44 分钟前
LeetCode三数之和-js题解
javascript·算法·leetcode
视频砖家1 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
小白变怪兽3 小时前
一、react18+项目初始化(vite)
前端·react.js
GISer_Jing4 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆4 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
我在北京coding5 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
海天胜景6 小时前
vue3 获取选中的el-table行数据
javascript·vue.js·elementui