Skip to content
js
debugger

// jsx转为createElement函数执行,这个函数返回element对象,children是特殊的element对象
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) =>
        typeof child === 'object' ? child : createTextElement(child)
      )
    }
  }
}

// createElement的children里处理文本地节点的时候,也是一个对象
function createTextElement(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  }
}

// 给fiber创建真实dom
function createDom(fiber) {
  const dom =
    fiber.type == 'TEXT_ELEMENT'
      ? document.createTextNode('')
      : document.createElement(fiber.type)

  // 处理dom和props,传入之前的props为空,当前的props
  updateDom(dom, {}, fiber.props)

  return dom
}

// 特殊的props - 事件
const isEvent = (key) => key.startsWith('on')

// 除了children喝事件的属性
const isProperty = (key) => key !== 'children' && !isEvent(key)

// 新的 props key
const isNew = (prev, next) => (key) => prev[key] !== next[key]

// 旧的 props key
const isGone = (prev, next) => (key) => !(key in next)

// 更新dom和props
function updateDom(dom, prevProps, nextProps) {
  // 删除旧的或者改变的事件监听器
  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])
    })

  // 删除旧的props
  Object.keys(prevProps)
    .filter(isProperty)
    .filter(isGone(prevProps, nextProps))
    .forEach((name) => {
      dom[name] = ''
    })

  // 设置新的或者改变的props
  Object.keys(nextProps)
    .filter(isProperty)
    .filter(isNew(prevProps, nextProps))
    .forEach((name) => {
      dom[name] = nextProps[name]
    })

  // 添加新的事件监听器
  Object.keys(nextProps)
    .filter(isEvent)
    .filter(isNew(prevProps, nextProps))
    .forEach((name) => {
      const eventType = name.toLowerCase().substring(2)
      dom.addEventListener(eventType, nextProps[name])
    })
}

// 构建完fiber树后,更新到页面,就是commit阶段
function commitRoot() {
  // 删除的fiber树组
  deletions.forEach(commitWork)

  // 提交更新
  commitWork(wipRoot.child)

  // commit完后,wipRoot树变成currentRoot tree,也就是旧的树
  currentRoot = wipRoot

  // wipRoot在commit后,就置为null
  wipRoot = null
}

// 提交更新,传入wipRoot.child
function commitWork(fiber) {
  if (!fiber) {
    return
  }

  let domParentFiber = fiber.parent

  // 因为函数组件是没有对应的dom的
  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)
}

// commitWork里处理删除的逻辑
function commitDeletion(fiber, domParent) {
  if (fiber.dom) {
    domParent.removeChild(fiber.dom)
  } else {
    commitDeletion(fiber.child, domParent)
  }
}

// 渲染函数
function render(element, container) {
  // 内存中的wipRoot fiber树
  wipRoot = {
    dom: container,
    props: {
      children: [element]
    },
    alternate: currentRoot // 第一次为null,第二次就是 之前的wipRoot fiber
  }

  // 每次执行的时候,重新置为空树组
  deletions = []

  // 下一个工作单元第一次为wipRoot fiber
  nextUnitOfWork = wipRoot
}

// 下一个工作单元
let nextUnitOfWork = null

// 旧fiber树
let currentRoot = null

// 当前fiber树
let wipRoot = null

// 删除的fiber树组
let deletions = null

// 循环任务
function workLoop(deadline) {
  let shouldYield = false

  // 有下一个工作单元并且不会被中断的时候
  while (nextUnitOfWork && !shouldYield) {
    // 构建fiber树
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork)

    // 是否中断
    shouldYield = deadline.timeRemaining() < 1
  }

  // 生成完fiber树后,一次性commit提交更新到页面
  if (!nextUnitOfWork && wipRoot) {
    commitRoot()
  }

  // 递归,生成fiber树
  requestIdleCallback(workLoop)
}

// 浏览器空闲时间执行
requestIdleCallback(workLoop)

// 执行工作单元
function performUnitOfWork(fiber) {
  // 执行函数组件处理
  const isFunctionComponent = fiber.type instanceof Function

  // 区分函数组件和类组件
  if (isFunctionComponent) {
    updateFunctionComponent(fiber)
  } else {
    updateHostComponent(fiber)
  }

  // 先儿子
  if (fiber.child) {
    return fiber.child
  }

  // 深度遍历,构建fiber树
  let nextFiber = fiber

  // 全部处理完fiber树
  while (nextFiber) {
    // 再兄弟
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }

    // 最后父亲的兄弟,然后又会去好父亲兄弟的儿子
    nextFiber = nextFiber.parent
  }
}

// 意思听起来是当前fiber
let wipFiber = null

// hook索引,因为是单向链表
let hookIndex = null

// 执行工作单元的时候,更新函数组件
function updateFunctionComponent(fiber) {
  // 当前fiber,函数组件也是一个fiber
  wipFiber = fiber

  // 索引置为0
  hookIndex = 0

  // wipFiber的hook置为空数组
  wipFiber.hooks = []

  // 构建子fiber树
  const children = [fiber.type(fiber.props)]

  // 调度,构建子fiber树
  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) => {
    // 把action添加到hook的对列里
    hook.queue.push(action)

    // 更新的时候,创建新的wipRoot fiber树
    wipRoot = {
      dom: currentRoot.dom,
      props: currentRoot.props,
      alternate: currentRoot
    }

    // 重置下一个工作单元
    nextUnitOfWork = wipRoot

    // 重置删除的树组
    deletions = []
  }

  // 当前fiber的hooks数组里添加hook
  wipFiber.hooks.push(hook)

  hookIndex++

  return [hook.state, setState]
}

// 更新类组件
function updateHostComponent(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber)
  }
  reconcileChildren(fiber, fiber.props.children)
}

// 调度 reconciliation
function reconcileChildren(wipFiber, elements) {
  let index = 0

  // 旧的fiber树
  let oldFiber = wipFiber.alternate && wipFiber.alternate.child

  let prevSibling = null

  // 同时迭代新的 element 树和旧的 fiber树
  while (index < elements.length || oldFiber != null) {
    const element = elements[index]

    // 当前fiber
    let newFiber = null

    // 类型相同:有oldFiber的时候,有element的时候,两个type相同的时候
    const sameType = oldFiber && element && element.type == oldFiber.type

    // 相同类型的dom,复用dom和props
    if (sameType) {
      newFiber = {
        type: oldFiber.type,
        props: element.props,
        dom: oldFiber.dom,
        parent: wipFiber,
        alternate: oldFiber,

        // 打上标记,后续commit更新
        effectTag: 'UPDATE'
      }
    }

    // 如果类型不同,但是有新fiber,说明要新增dom
    if (element && !sameType) {
      newFiber = {
        type: element.type,
        props: element.props,
        dom: null,
        parent: wipFiber,
        alternate: null,

        // 打上标记,后续commit更新
        effectTag: 'PLACEMENT'
      }
    }

    // 类型不同,旧的还有,打上删除标记
    if (oldFiber && !sameType) {
      oldFiber.effectTag = 'DELETION'

      // 因为没有新fiber,所以要存全局变量树组里
      deletions.push(oldFiber)
    }

    // 如果有旧fiber,这里是while循环用的
    if (oldFiber) {
      oldFiber = oldFiber.sibling
    }

    // 处理child,sibling关系
    if (index === 0) {
      // 虽然函数组件的type是函数,但不影响它的child是fiber
      wipFiber.child = newFiber
    } else if (element) {
      prevSibling.sibling = newFiber
    }

    prevSibling = newFiber
    index++
  }
}

// myReact
const MyReact = {
  createElement,
  render,
  useState
}

/** @jsx MyReact.createElement */
function Counter() {
  const [state, setState] = MyReact.useState(1)
  const [state1, setState1] = MyReact.useState(2)
  return (
    <div>
      <h1
        onClick={() => {
          setState((c) => c + 1)
        }}
        style="user-select: none"
      >
        Count: {state}
      </h1>
      <h2
        onClick={() => {
          setState1((c) => c + 2)
        }}
        style="user-select: none"
      >
        Count: {state1}
      </h2>
    </div>
  )
}

const element = <Counter />

const container = document.getElementById('root')

MyReact.render(element, container)
debugger

// jsx转为createElement函数执行,这个函数返回element对象,children是特殊的element对象
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) =>
        typeof child === 'object' ? child : createTextElement(child)
      )
    }
  }
}

// createElement的children里处理文本地节点的时候,也是一个对象
function createTextElement(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  }
}

// 给fiber创建真实dom
function createDom(fiber) {
  const dom =
    fiber.type == 'TEXT_ELEMENT'
      ? document.createTextNode('')
      : document.createElement(fiber.type)

  // 处理dom和props,传入之前的props为空,当前的props
  updateDom(dom, {}, fiber.props)

  return dom
}

// 特殊的props - 事件
const isEvent = (key) => key.startsWith('on')

// 除了children喝事件的属性
const isProperty = (key) => key !== 'children' && !isEvent(key)

// 新的 props key
const isNew = (prev, next) => (key) => prev[key] !== next[key]

// 旧的 props key
const isGone = (prev, next) => (key) => !(key in next)

// 更新dom和props
function updateDom(dom, prevProps, nextProps) {
  // 删除旧的或者改变的事件监听器
  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])
    })

  // 删除旧的props
  Object.keys(prevProps)
    .filter(isProperty)
    .filter(isGone(prevProps, nextProps))
    .forEach((name) => {
      dom[name] = ''
    })

  // 设置新的或者改变的props
  Object.keys(nextProps)
    .filter(isProperty)
    .filter(isNew(prevProps, nextProps))
    .forEach((name) => {
      dom[name] = nextProps[name]
    })

  // 添加新的事件监听器
  Object.keys(nextProps)
    .filter(isEvent)
    .filter(isNew(prevProps, nextProps))
    .forEach((name) => {
      const eventType = name.toLowerCase().substring(2)
      dom.addEventListener(eventType, nextProps[name])
    })
}

// 构建完fiber树后,更新到页面,就是commit阶段
function commitRoot() {
  // 删除的fiber树组
  deletions.forEach(commitWork)

  // 提交更新
  commitWork(wipRoot.child)

  // commit完后,wipRoot树变成currentRoot tree,也就是旧的树
  currentRoot = wipRoot

  // wipRoot在commit后,就置为null
  wipRoot = null
}

// 提交更新,传入wipRoot.child
function commitWork(fiber) {
  if (!fiber) {
    return
  }

  let domParentFiber = fiber.parent

  // 因为函数组件是没有对应的dom的
  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)
}

// commitWork里处理删除的逻辑
function commitDeletion(fiber, domParent) {
  if (fiber.dom) {
    domParent.removeChild(fiber.dom)
  } else {
    commitDeletion(fiber.child, domParent)
  }
}

// 渲染函数
function render(element, container) {
  // 内存中的wipRoot fiber树
  wipRoot = {
    dom: container,
    props: {
      children: [element]
    },
    alternate: currentRoot // 第一次为null,第二次就是 之前的wipRoot fiber
  }

  // 每次执行的时候,重新置为空树组
  deletions = []

  // 下一个工作单元第一次为wipRoot fiber
  nextUnitOfWork = wipRoot
}

// 下一个工作单元
let nextUnitOfWork = null

// 旧fiber树
let currentRoot = null

// 当前fiber树
let wipRoot = null

// 删除的fiber树组
let deletions = null

// 循环任务
function workLoop(deadline) {
  let shouldYield = false

  // 有下一个工作单元并且不会被中断的时候
  while (nextUnitOfWork && !shouldYield) {
    // 构建fiber树
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork)

    // 是否中断
    shouldYield = deadline.timeRemaining() < 1
  }

  // 生成完fiber树后,一次性commit提交更新到页面
  if (!nextUnitOfWork && wipRoot) {
    commitRoot()
  }

  // 递归,生成fiber树
  requestIdleCallback(workLoop)
}

// 浏览器空闲时间执行
requestIdleCallback(workLoop)

// 执行工作单元
function performUnitOfWork(fiber) {
  // 执行函数组件处理
  const isFunctionComponent = fiber.type instanceof Function

  // 区分函数组件和类组件
  if (isFunctionComponent) {
    updateFunctionComponent(fiber)
  } else {
    updateHostComponent(fiber)
  }

  // 先儿子
  if (fiber.child) {
    return fiber.child
  }

  // 深度遍历,构建fiber树
  let nextFiber = fiber

  // 全部处理完fiber树
  while (nextFiber) {
    // 再兄弟
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }

    // 最后父亲的兄弟,然后又会去好父亲兄弟的儿子
    nextFiber = nextFiber.parent
  }
}

// 意思听起来是当前fiber
let wipFiber = null

// hook索引,因为是单向链表
let hookIndex = null

// 执行工作单元的时候,更新函数组件
function updateFunctionComponent(fiber) {
  // 当前fiber,函数组件也是一个fiber
  wipFiber = fiber

  // 索引置为0
  hookIndex = 0

  // wipFiber的hook置为空数组
  wipFiber.hooks = []

  // 构建子fiber树
  const children = [fiber.type(fiber.props)]

  // 调度,构建子fiber树
  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) => {
    // 把action添加到hook的对列里
    hook.queue.push(action)

    // 更新的时候,创建新的wipRoot fiber树
    wipRoot = {
      dom: currentRoot.dom,
      props: currentRoot.props,
      alternate: currentRoot
    }

    // 重置下一个工作单元
    nextUnitOfWork = wipRoot

    // 重置删除的树组
    deletions = []
  }

  // 当前fiber的hooks数组里添加hook
  wipFiber.hooks.push(hook)

  hookIndex++

  return [hook.state, setState]
}

// 更新类组件
function updateHostComponent(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber)
  }
  reconcileChildren(fiber, fiber.props.children)
}

// 调度 reconciliation
function reconcileChildren(wipFiber, elements) {
  let index = 0

  // 旧的fiber树
  let oldFiber = wipFiber.alternate && wipFiber.alternate.child

  let prevSibling = null

  // 同时迭代新的 element 树和旧的 fiber树
  while (index < elements.length || oldFiber != null) {
    const element = elements[index]

    // 当前fiber
    let newFiber = null

    // 类型相同:有oldFiber的时候,有element的时候,两个type相同的时候
    const sameType = oldFiber && element && element.type == oldFiber.type

    // 相同类型的dom,复用dom和props
    if (sameType) {
      newFiber = {
        type: oldFiber.type,
        props: element.props,
        dom: oldFiber.dom,
        parent: wipFiber,
        alternate: oldFiber,

        // 打上标记,后续commit更新
        effectTag: 'UPDATE'
      }
    }

    // 如果类型不同,但是有新fiber,说明要新增dom
    if (element && !sameType) {
      newFiber = {
        type: element.type,
        props: element.props,
        dom: null,
        parent: wipFiber,
        alternate: null,

        // 打上标记,后续commit更新
        effectTag: 'PLACEMENT'
      }
    }

    // 类型不同,旧的还有,打上删除标记
    if (oldFiber && !sameType) {
      oldFiber.effectTag = 'DELETION'

      // 因为没有新fiber,所以要存全局变量树组里
      deletions.push(oldFiber)
    }

    // 如果有旧fiber,这里是while循环用的
    if (oldFiber) {
      oldFiber = oldFiber.sibling
    }

    // 处理child,sibling关系
    if (index === 0) {
      // 虽然函数组件的type是函数,但不影响它的child是fiber
      wipFiber.child = newFiber
    } else if (element) {
      prevSibling.sibling = newFiber
    }

    prevSibling = newFiber
    index++
  }
}

// myReact
const MyReact = {
  createElement,
  render,
  useState
}

/** @jsx MyReact.createElement */
function Counter() {
  const [state, setState] = MyReact.useState(1)
  const [state1, setState1] = MyReact.useState(2)
  return (
    <div>
      <h1
        onClick={() => {
          setState((c) => c + 1)
        }}
        style="user-select: none"
      >
        Count: {state}
      </h1>
      <h2
        onClick={() => {
          setState1((c) => c + 2)
        }}
        style="user-select: none"
      >
        Count: {state1}
      </h2>
    </div>
  )
}

const element = <Counter />

const container = document.getElementById('root')

MyReact.render(element, container)