# 函数组件

目前我们还只考虑了直接渲染 DOM 标签的情况,不支持组件,而组件是 React 的灵魂,下面我们来实现函数组件。

以一个非常简单的组件代码为例。

/** @jsx Redact.createElement */
function App(props) {
  return <h1>Hi {props.name}</h1>;
};

// 等效 JS 代码👇
function App(props) {
  return Redact.craeteElement(
    "h1",
    null,
    "Hi ",
    props.name
  );
}

const element = <App name="foo" />;
const container = document.getElementById("root");
Redact.render(element, container);

函数组件有2个不同点:

  • 函数组件的 fiber 节点没有对应的 DOM
  • 函数组件的 children 来自函数执行结果,而不像标签元素一样直接从 props获取,因为 children 不只是函数组件使用时包含的子孙节点,还需要组合组件本身的结构

注意:以下代码省略了未改动部分。

function commitWork(fiber) {
  if (!fiber) {
    return;
  }
  
  // 当 fiber 是函数组件时节点不存在 DOM
  // 故需要遍历父节点以找到最近的有 DOM 的节点
  let domParentFiber = fiber.parent;
  while (!domParentFiber.dom) {
    domParentFiber = domParentFiber.parent;
  }
  const domParent = domParentFiber.dom;
  if (fiber.effectTag === "PLACEMENT" && fiber.dom != null) {
    domParent.appendChild(fiber.dom);
  } else if (fiber.effectTag === "UPDATE" && fiber.dom != null) {
    updateDom(fiber.dom, fiber.alternate.props, fiber.props);
  } else if (fiber.effectTag === "DELETION") {
    // 直接移除 DOM 替换成 commitDeletion 函数
    commitDeletion(fiber, domParent);
  }
  
  commitWork(fiber.child);
  commitWork(fiber.sibling);
}

// 新增函数,移除 DOM 节点
function commitDeletion(fiber, domParent) {
  // 当 child 是函数组件时不存在 DOM
  // 故需要递归遍历子节点找到真正的 DOM
  if (fiber.dom) {
    domParent.removeChild(fiber.dom);
  } else {
    commitDeletion(fiber.child, domParent);
  }
}

function performUnitOfWork(fiber) {
  const isFunctionComponent = fiber.type instanceof Function;
  // 原本逻辑挪到 updateHostComponent 函数
  if (isFunctionComponent) {
    updateFunctionComponent(fiber);
  } else {
    updateHostComponent(fiber);
  }
  if (fiber.child) {
    return fiber.child;
  }
  let nextFiber = fiber;
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling;
    }
    nextFiber = nextFiber.parent;
  }
}

// 新增函数,处理函数组件
function updateFunctionComponent(fiber) {
  // 执行函数组件得到 children
  const children = [fiber.type(fiber.props)];
  reconcileChildren(fiber, children);
}

// 新增函数,处理原生标签组件
function updateHostComponent(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  reconcileChildren(fiber, fiber.props.children);
}
最近更新时间: 2020/5/26 19:03:58