/** @jsxRuntime classic */

//**============== */

const Didact = {
  createElement,
  render,
};

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  };
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) =>
        typeof child === "object" ? child : createTextElement(child)
      ),
    },
  };
}

/** @jsx Didact.createElement */
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
);
// const element = Didact.createElement(
//   "div",
//   { id: "foo" },
//   Didact.createElement("a", null, "bar"),
//   Didact.createElement("b")
// );

function createDom(fiber) {
  const dom =
    fiber.type == "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(fiber.type);

  const isProperty = (key) => key !== "children";
  Object.keys(fiber.props)
    .filter(isProperty)
    .forEach((name) => {
      dom[name] = fiber.props[name];
    });

  return dom;
}

function commitRoot() {
  commitWork(wipRoot.child);
  wipRoot = null;
}

function commitWork(fiber) {
  if (!fiber) {
    return;
  }
  const domParent = fiber.parent.dom;
  domParent.appendChild(fiber.dom);
  commitWork(fiber.child);
  commitWork(fiber.sibling);
}

let nextUnitOfWork = null;
let wipRoot = null;
let currentRoot = null;

function workLoop(deadline) {
  let shouldYield = false;
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    shouldYield = deadline.timeRemaining() < 1;
  }
  if (!nextUnitOfWork && wipRoot) {
    commitRoot();
  }
  requestIdleCallback(workLoop);
}

requestIdleCallback(workLoop);

//获取工作的第一个节点 执行工作,而且返回下一个工作单元
function performUnitOfWork(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  const elements = fiber.props.children;
  let index = 0;
  let prevSibling = null;
  while (index < elements.length) {
    const element = elements[index];
    const newFiber = {
      type: element.type,
      props: element.props,
      parent: fiber,
      dom: null,
    };
    if (index === 0) {
      fiber.child = newFiber;
    } else {
      prevSibling.sibling = newFiber;
    }
    prevSibling = newFiber;
    index++;
  }
  if (fiber.child) {
    return fiber.child;
  }
  let nextFiber = fiber;
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling;
    }
    nextFiber = nextFiber.parent;
  }
}

function render(element, container) {
  wipRoot = {
    dom: container,
    props: {
      children: [element],
    },
    alternate: currentRoot,
  };
  nextUnitOfWork = wipRoot;
}

const container = document.getElementById("root");
Didact.render(element, container);

React 17 版本开始提供了一个新的JSX转换方案。在之前,JSX 代码会被默认编译成 React.createElement(...) ,而使用新 JSX 方案后,会从一个独立的模块react/jsx-runtime中引入 JSX 函数。

babel插件@babel/plugin-transform-react-jsx

/** @jsxRuntime classic */  babel运行时转换

render 创建最初始的fiber对象,render执行完之后会执行workLoop方法,开发fiber任务

function render(element, container) {
  wipRoot = {
    dom: container,
    props: {
      children: [element],
    },
    alternate: currentRoot,
  };
  nextUnitOfWork = wipRoot;
}

如果存在下一个fiber任务那么就行执行performUnitOfWork,如果没有说明fiber树构建完毕,执行commitRoot 提交进行页面渲染。

function workLoop(deadline) {
  let shouldYield = false;
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    shouldYield = deadline.timeRemaining() < 1;
  }
  if (!nextUnitOfWork && wipRoot) {
    commitRoot();
  }
  requestIdleCallback(workLoop);
}

performUnitOfWork构建fiber树且返回下一个fiber任务

//获取工作的第一个节点 执行工作,而且返回下一个工作单元
function performUnitOfWork(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  const elements = fiber.props.children;
  let index = 0;
  let prevSibling = null;
  while (index < elements.length) {
    const element = elements[index];
    const newFiber = {
      type: element.type,
      props: element.props,
      parent: fiber,
      dom: null,
    };
    if (index === 0) {
      fiber.child = newFiber;
    } else {
      prevSibling.sibling = newFiber;
    }
    prevSibling = newFiber;
    index++;
  }
  if (fiber.child) {
    return fiber.child;
  }
  let nextFiber = fiber;
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling;
    }
    nextFiber = nextFiber.parent;
  }
}

fiber对象和真实的dom对象挂钩。

 if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }

处理fiber对象的子元素,且为对应的子元素创建fiber对象,这个时候子元素fiber对象的dom属性还是空。如果是第一个子元素,那么把第一个子元素的fiber对象挂到父fiber对象的child属性上。如果不是父fiber的第一个子元素,那么把该fiber对象挂到它前面一个fiber对象的sibling属性上。(也就是只创建了父fiber直接子元素对应的fiber对象,但是还没有处理子元素对应的fiber任务)

let index = 0;
  let prevSibling = null;
  while (index < elements.length) {
    const element = elements[index];
    const newFiber = {
      type: element.type,
      props: element.props,
      parent: fiber,
      dom: null,
    };
    if (index === 0) {
      fiber.child = newFiber;
    } else {
      prevSibling.sibling = newFiber;
    }
    prevSibling = newFiber;
    index++;
  }

 返回下一个fiber任务,如果有子元素,那么处理第一个子元素。如果没有子元素,那么处理它兄弟节点,如果没有兄弟节点,那么返回父节点。然后寻找父元素的兄弟节点,一直找到最初始节点。当performUnitOfWork没有可以返回的节点,那么说明fiber树构建完成,执行commitRoot渲染页面。(每一个fiber任务处理完都可以中断,如果恢复,那就是继续处理nextUnitOfWork。所以fiber任务的终断和恢复是在fiber树构建阶段)

 if (fiber.child) {
    return fiber.child;
  }
  let nextFiber = fiber;
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling;
    }
    nextFiber = nextFiber.parent;
  }

页面渲染:

commitWork(wipRoot.child); //从最外层开始

wipRoot = null;//开始渲染要把wipRoot置为null,因为下一次页面刷新还会重新构建。

const domParent = fiber.parent.dom;

domParent.appendChild(fiber.dom);

commitWork(fiber.child);

commitWork(fiber.sibling);

通过递归添加子dom节点到父dom上面。最后完成渲染。(这个时候是无法中断的,其实这里也可以实现中断,只是这里没有实现,原理和上面一样,所有处理绑定在一个对象上面,就可以实现中断和恢复)


function commitRoot() {
  commitWork(wipRoot.child);
  wipRoot = null;
}

function commitWork(fiber) {
  if (!fiber) {
    return;
  }
  const domParent = fiber.parent.dom;
  domParent.appendChild(fiber.dom);
  commitWork(fiber.child);
  commitWork(fiber.sibling);
}

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐