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

相关推荐
nicole bai几秒前
dialog封装
前端·javascript·vue.js
我命由我1234517 分钟前
JS Mock(Mock 拦截请求的前提、Mock 使用、关闭 Mock)
开发语言·前端·javascript·前端框架·html·html5·js
开开心心就好23 分钟前
手机精确倒计时工具悬浮窗口秒数实时显示
android·前端·javascript·jupyter·智能手机·pdf·html
索西引擎1 小时前
【Vue】Vue.js 3 声明式渲染架构的设计原理与模块协同机制研究
前端·javascript·vue.js
zzzzzz3102 小时前
react-bits:从 36K stars 的“酷炫组件”,看动效如何成为 React 的可复用能力
react.js·开源·动效
mayaairi3 小时前
Vue2 事件绑定完全指南:从入门到原理
前端·javascript·vue.js
ℋᙚᵐⁱᒻᵉ鲸落9 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙10 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
এ慕ོ冬℘゜11 小时前
navigator 导航对象 BOM制作方法
javascript