参考博客orz: 传送门
包含useEffect、useState,后续更新diff。
            
            
              js
              
              
            
          
          // 创建虚拟dom的函数 
// 函数组件会被babel最初解析成一个特殊的对象,因为还没有运行函数
// function createElement(type, props, ...children) {
//   // console.log(type)
//   return {
//     type,
//     props: {
//       ...props,
//       children: children.map((child) =>
//         typeof child === "object" ? child : createTextElement(child)
//       ),
//     },
//   };
// }
function createElement(type, props, ...children) {
  // console.log("debug: ", children, children.flat()); 
  return {
    type, 
    props: {
      ...(props || {}), 
      children: children.flat().map(i => typeof i === 'object' && i !== null ? i : createTextElement(i))
    }
  }
}
function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  };
}
function createDom(fiber) {
  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) {
  prevProps = prevProps || {}; 
  nextProps = 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);
      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 commitEffects() {
  const _ = (fiber) => {
    if(!fiber || fiber === null) return;
    if(fiber && fiber.hooks && fiber.type instanceof Function) {
      fiber.hooks.filter(hk => hk.isEffect === true).forEach(hk => {
        let nx = null; 
        if(hk.cb) {
          nx = hk.cb();
          hk.cb = null;
        }
        if(nx) {
          if(hk.clean) hk.clean(); 
          hk.clean = nx; 
        }
      });
    }
    _(fiber.child);
    _(fiber.sibling);
  };
  _(wipRoot);
}
function commitRoot() {
  deletions.forEach(commitWork);
  commitWork(wipRoot.child);
  commitEffects();
  currentRoot = wipRoot;
  wipRoot = null;
}
function commitWork(fiber) {
  if (!fiber) {
    return;
  }
  let domParentFiber = fiber.parent;
  // console.log("si: ", fiber.parent)
  while (!domParentFiber.dom) {
    domParentFiber = domParentFiber.parent;
  }
  const domParent = domParentFiber.dom; // 找到第一个有真实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);
  }
}
// 将element部署到container上,只会在加载页面的时候进行一次。
function render(element, container) {
  // console.log("debug: ", element, container);
  // console.log("debug: ", currentRoot);
  // 初始化工作区的fiber-tree
  wipRoot = {
    dom: container, // root
    props: {
      children: [element],
    },
    alternate: currentRoot,
  };
  deletions = []; // 需要删除的元素
  nextUnitOfWork = wipRoot; // 下一个需要工作的fiber节点
}
let nextUnitOfWork = null; // 下一次工作的目标fiber,初始化为null
let currentRoot = null; // 当前渲染的fiber-dom树
let wipRoot = null; // 正在工作的fiber-dom树
let deletions = null; // 需要删除的元素的数组
// 工作片,最小工作单元
function workLoop(deadline) {
  // console.log(deadline);
  let shouldYield = false; // 初始化为false,当为ture的时候需要暂停
  // 当有下一个工作的单元并且不需要暂停时
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork); // 执行nextUnitOfWork
    shouldYield = deadline.timeRemaining() < 1;
  }
  if (!nextUnitOfWork && wipRoot) {
    commitRoot();
  }
  requestIdleCallback(workLoop);
}
// 浏览器函数,每次自动运行回调
requestIdleCallback(workLoop);
// 执行fiber任务的函数,返回下一个工作的单元
function performUnitOfWork(fiber) {
  const isFunctionComponent = fiber.type instanceof Function;
  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;
  }
}
let wipFiber = null; // 当前工作的wib-fiber
let hookIndex = null;
function updateFunctionComponent(fiber) {
  // console.log(fiber);
  wipFiber = fiber;
  hookIndex = 0;
  wipFiber.hooks = [];
  // console.log(fiber);
  const children = [fiber.type(fiber.props)]; // 函数组件返回的jsx元素
  // console.log("debug", children);
  // console.log("de: ", children);
  reconcileChildren(fiber, children.flat()); 
}
/**
  useEffect(() => {
    console.log("执行");
  }, [])  
*/
function useEffect(f = () => {}, arr = []) {
  const oldHook = wipFiber.alternate 
  && wipFiber.alternate.hooks
  && wipFiber.alternate.hooks[hookIndex];  // 拿到旧的Effect
  let flag = false;
  if(!oldHook || 
    (oldHook.deps && (oldHook.deps.length !== arr.length
      || oldHook.deps.some((i, idx) => (i !== arr[idx]))
    ))
  ) flag = true; 
  console.log(flag)
  const hook = {
    deps: arr, 
    cb: flag ? f : null, 
    clean: oldHook ? oldHook.clean : null, 
    isEffect: true
  };
  if(!wipFiber.hooks) wipFiber.hooks = []; 
  wipFiber.hooks.push(hook); 
  hookIndex ++; 
}
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);
}
// 当前的fiber, 新节点的子元素 
function reconcileChildren(wipFiber, elements) {
  /**
   * diff 算法:
   * 给定两个列表,每个元素都有自己的key,要求尽可能复用原本的元素。
   * 对所有的key-type哈希成数值/带字母的,映射到它们对应的新的节点的索引上面,无key值的只能将其删除重新创建dom
   * map, 字典树
  */
  let index = 0;
  // 通过alternate链接旧的fiber
  let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
  let prevSibling = null;
  // console.log("reconcileChildren: ", oldFiber, elements);
  // 遍历新的elements,并且去寻找新的
  while (index < elements.length || oldFiber != null) {
    const element = elements[index]; // 拿到新的节点,注意此时还没有更新
    let newFiber = null; // 新fiber的初始化
    // 判断两者是否相等
    const sameType = oldFiber && element && element.type == oldFiber.type;
    // 两者相等的时候,可以复用真实dom,改下props就好。
    if (sameType) {
      newFiber = {
        type: oldFiber.type, // 复用,因为相等
        props: element.props,
        dom: oldFiber.dom,
        parent: wipFiber,
        alternate: oldFiber, 
        effectTag: "UPDATE", // 标记为UPDATE 
      };
    }
    if (element && !sameType) {
      // console.log("替换 ", );
      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,
  useEffect
};
/** @jsx Didact.createElement */
function Foo() {
  const [s1, setS1] = Didact.useState(2);
  return (
    <div>
      <div onClick={() => setS1((c) => (c += "1"))}>code__ccc</div>
      <h2> {s1} </h2>
    </div>
  );
}
/** @jsx Didact.createElement */
function Counter(props) {
  const [state, setState] = Didact.useState(1);
  const [isShow, setShow] = Didact.useState(true);
  Didact.useEffect(() => {
    console.log("count发生了改变", state);
  }, [state]) 
  return (
    <div>
      <h1 onClick={() => setState((c) => c + 1)} style="user-select: none">
        Count: {state}
        {/* <Foo /> */}
      </h1>
      {[1, 2, 3, 4].map((i, idx) => <li key={idx}>{i}</li>)} 
      {null}
      <div>
        {
          isShow ? <div>这个是一个div</div> : null
        }
      </div>
      <button onClick={() => {
        setShow((isShow) => {
          console.log("执行了");
          return !isShow;
        });
      }}>
        按钮
      </button>
    </div>
  );
}
const element = <Counter x={1} />;
// const ele = <p>急啊纠结啊</p>; 
const container = document.getElementById("root");
Didact.render(element, container);