function createElement(type, props, ...children) { return { type, props: { ...props, children: children.map(child => typeof child === "object" ? child : createTextElement(child) ), }, }}function createTextElement(text) { return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, }}function createDom(fiber) { // 判断dom 是否为文本节点 const dom = fiber.type == "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(fiber.type) updateDom(dom, {}, fiber.props) return dom}const isEvent = key => key.startsWith("on")const isProperty = key =>key !== "children" && !isEvent(key)const isNew = (prev, next) => key =>prev[key] !== next[key]const isGone = (prev, next) => key => !(key in next)function updateDom(dom, prevProps, nextProps) { //Remove old or changed event listeners Object.keys(prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) //从第三个字符串开始截取 onClick -> click // 移除事件 dom.removeEventListener( eventType, prevProps[name] ) }) // Remove old properties Object.keys(prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set new or changed properties Object.keys(nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add event listeners Object.keys(nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.addEventListener( eventType, nextProps[name] ) })}function commitRoot() { deletions.forEach(commitWork) commitWork(wipRoot.child) currentRoot = wipRoot wipRoot = null}function commitWork(fiber) { if (!fiber) { return } 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") { commitDeletion(fiber, domParent) } commitWork(fiber.child) commitWork(fiber.sibling)}function commitDeletion(fiber, domParent) { if (fiber.dom) { domParent.removeChild(fiber.dom) } else { commitDeletion(fiber.child, domParent) }}function render(element, container) { wipRoot = { dom: container, props: { children: [element], }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot}let nextUnitOfWork = nulllet currentRoot = nulllet wipRoot = nulllet deletions = null// nextUnitOfWork 下一个需要处理的fiber节点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) { // 判断是否是函数组件 const isFunctionComponent = fiber.type instanceof Function if (isFunctionComponent) { updateFunctionComponent(fiber) } else { updateHostComponent(fiber) } // 如果这个fiber有子节点 if (fiber.child) { return fiber.child } // 如果fiber没有子节点 let nextFiber = fiber while (nextFiber) { // 判断是否有兄弟节点,如果有则返回兄弟节点 if (nextFiber.sibling) { return nextFiber.sibling } //否则给 nextFiber 赋值为父节点,此时:while条件 判断父节点的兄弟节点 nextFiber = nextFiber.parent }}// workInProgressFiber let wipFiber = nulllet hookIndex = nullfunction updateFunctionComponent(fiber) { wipFiber = fiber // 默认把fiber给wipFiber hookIndex = 0 // 默认把 hookIndex 赋值为0 wipFiber.hooks = [] const children = [fiber.type(fiber.props)] reconcileChildren(fiber, children)}function useState(initial) { const oldHook = wipFiber.alternate && wipFiber.alternate.hooks && wipFiber.alternate.hooks[hookIndex] const hook = { state: oldHook ? oldHook.state : initial, queue: [], } const actions = oldHook ? oldHook.queue : [] actions.forEach(action => { hook.state = action(hook.state) }) const setState = action => { hook.queue.push(action) wipRoot = { dom: currentRoot.dom, props: currentRoot.props, alternate: currentRoot, } nextUnitOfWork = wipRoot deletions = [] } wipFiber.hooks.push(hook) hookIndex++ return [hook.state, setState]}function updateHostComponent(fiber) { if (!fiber.dom) { fiber.dom = createDom(fiber) } reconcileChildren(fiber, fiber.props.children)}function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = wipFiber.alternate && wipFiber.alternate.child let prevSibling = null while ( index < elements.length || oldFiber != null ) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && element.type == oldFiber.type if (sameType) { newFiber = { type: oldFiber.type, props: element.props, dom: oldFiber.dom, parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", // 标记节点是需要更新 } } if (element && !sameType) { newFiber = { type: element.type, props: element.props, dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { oldFiber.effectTag = "DELETION" deletions.push(oldFiber) } if (oldFiber) { oldFiber = oldFiber.sibling } if (index === 0) { wipFiber.child = newFiber } else if (element) { prevSibling.sibling = newFiber } prevSibling = newFiber index++ }}const Didact = { createElement, render, useState,}/** @jsx Didact.createElement */function Counter() { const [state, setState] = Didact.useState(1) return ( <h1 onClick={() => setState(c => c + 1)}> Count: {state}</h1>)}const element = <Counter /> const container = document.getElementById("root")Didact.render(element, container)