手搓螺旋丸

coderljw 2024-10-13 手写代码
  • 手写代码
大约 10 分钟

# 1. Object.create

function object(o) {
  function F() {}
  F.prototype = o
  return new F()
}
1
2
3
4
5

# 2. new

function _new(Fn, ...args) {
  const obj = Object.create(Fn.prototype)
  Fn.apply(obj, args)
  return obj
}
1
2
3
4
5

# 3. instanceof

function _instanceof(left, right) {
  let proto = Object.getPrototypeOf(left)
  const prototype = right.prototype

  while (true) {
    if (!proto) return false
    if (proto === prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
}
1
2
3
4
5
6
7
8
9
10

# 4. call、apply、bind

Function.prototype.myCall = function (context = window, ...args) {
  const fn = Symbol('fn')
  context[fn] = this
  context[fn](...args)
  delete context[fn]
}

Function.prototype.myApply = function (context = window, args = []) {
  const fn = Symbol('fn')
  context[fn] = this
  context[fn](...args)
  delete context[fn]
}

Function.prototype.myBind = function (context, ...outerArgs) {
  return (...innerArgs) => this.myApply(context, [...outerArgs, ...innerArgs])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5. Clone

// 类型检测
const detectionType = value =>
  typeof value === 'object'
    ? {}.toString.call(value).slice(8, -1).toLowerCase()
    : typeof value

// 浅克隆
function shallowClone(obj) {
  const type = detectionType(obj),
    Ctor = obj.constructor,
    typeMap = new Map(
      Object.entries({
        [['symbol', 'bigint']]: _ => Object(obj),
        [['regexp', 'date']]: _ => new Ctor(obj),
        [['error']]: _ => new Ctor(obj.message),
        [['function']]: _ => {
          return function (...args) {
            return obj.apply(this, args)
          }
        },
        [['object', 'array']]: _ => {
          const keys = [
              ...Object.keys(obj),
              ...Object.getOwnPropertySymbols(obj),
            ],
            result = new Ctor()
          keys.forEach(key => (result[key] = obj[key]))
          return result
        },
        [['set', 'map']]: type => {
          const result = new Ctor()
          for (const [key, value] of obj.entries()) {
            type === 'set' ? result.add(value) : result.set(key, value)
          }
          return result
        },
      })
    )

  for (const [key, value] of typeMap.entries()) {
    if (key.includes(type)) return value(type)
  }

  return obj
}

// 深克隆
function deepClone(obj, cache = new Set()) {
  const type = detectionType(obj),
    Ctor = obj.constructor

  if (!/^(object|array|set|map)$/i.test(type)) return shallowClone(obj)

  // 防止循环引用
  if (cache.has(obj)) return obj
  cache.add(obj)

  const result = new Ctor()
  if (/^(object|array)$/i.test(type)) {
    const keys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]
    keys.forEach(key => (result[key] = deepClone(obj[key], cache)))
  } else {
    for (const [key, value] of obj.entries()) {
      type === 'set'
        ? result.add(deepClone(value, cache))
        : result.set(key, deepClone(value, cache))
    }
  }

  return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 6. JSON

  • JSON.stringify(新增 BigInt 和循环引用错误处理)。

    // 类型检测
    const detectionType = value =>
      typeof value === 'object'
        ? {}.toString.call(value).slice(8, -1).toLowerCase()
        : typeof value
    
    function jsonStringify(obj, cache = new Set()) {
      const type = detectionType(obj),
        typeMap = new Map(
          Object.entries({
            [['string']]: value => `"${value}"`,
            [['number', 'boolean']]: value => `${value}`,
            [['undefined', 'symbol', 'function', 'bigint']]: _ => void 0,
            [['date']]: value => `"${value.toISOString()}"`,
          })
        )
    
      // 防止循环引用
      if (/^(array|object)$/i.test(type) && cache.has(obj)) {
        return '{}'
      }
      cache.add(obj)
    
      if (type === 'array') {
        const result = obj.map(value => {
          if (
            /^(undefined|symbol|function|bigint)$/i.test(detectionType(value))
          ) {
            return 'null'
          }
          return jsonStringify(value, cache)
        })
        return `[${result.join()}]`
      }
    
      if (type === 'object') {
        const result = {}
        for (const [key, value] of Object.entries(obj)) {
          if (
            !/^(undefined|symbol|function|bigint)$/i.test(detectionType(value))
          ) {
            result[key] = jsonStringify(value, cache)
          }
        }
        return `{${Object.entries(result)
          .map(i => {
            i[0] = `"${i[0]}"`
            return i.join(':')
          })
          .join()}}`
      }
    
      if ([null, NaN, Infinity, -Infinity].some(i => Object.is(i, obj))) {
        return 'null'
      }
    
      let match
      for (const [key, value] of typeMap.entries()) {
        if (key.includes(type)) {
          if (!match) match = true
          return value(obj)
        }
      }
    
      if (!match) return '{}'
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
  • JSON.parse

    // 方式一
    function jsonParse(obj) {
      return eval(`(${obj})`)
    }
    
    // 方式二
    function jsonParse(obj) {
      return new Function(`return ${obj}`)()
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 7. Promise

class MyPromise {
  static PENDING = 'PENDING'
  static FULFILLED = 'FULFILLED'
  static REJECTED = 'REJECTED'

  status = MyPromise.PENDING
  value = null
  callbacks = []

  constructor(executor) {
    const createExeFn = (status, handlerProp) => value => {
      if (this.status === MyPromise.PENDING) {
        this.status = status
        this.value = value
        setTimeout(() => this.callbacks.forEach(cb => cb[handlerProp](value)))
      }
    }
    try {
      executor(
        createExeFn(MyPromise.FULFILLED, 'onFulfilled'),
        createExeFn(MyPromise.REJECTED, 'onRejected')
      )
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') onFulfilled = _ => this.value
    if (typeof onRejected !== 'function') onRejected = _ => this.value

    return new MyPromise((resolve, reject) => {
      const statusMap = {
        [MyPromise.PENDING]: _ => {
          this.callbacks.push({
            onFulfilled: value =>
              this.#shunt(onFulfilled(value), resolve, reject),
            onRejected: reason =>
              this.#shunt(onRejected(reason), resolve, reject),
          })
        },
        [MyPromise.FULFILLED]: _ =>
          setTimeout(_ =>
            this.#shunt(onFulfilled(this.value), resolve, reject)
          ),
        [MyPromise.REJECTED]: _ =>
          setTimeout(_ => this.#shunt(onRejected(this.value), resolve, reject)),
      }
      statusMap[this.status]()
    })
  }
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      if (value instanceof MyPromise) resolve.then(value)
      else resolve(value)
    })
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const results = []
      let count = 0
      promises.forEach((promise, index) => {
        promise.then(result => {
          results[index] = result
          count++
          count >= promises.length && resolve(results)
        }, reject)
      })
    })
  }
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => promise.then(resolve, reject))
    })
  }
  #shunt(result, resolve, reject) {
    try {
      if (result instanceof MyPromise) result.then(resolve, reject)
      else resolve(result)
    } catch (error) {
      reject(error)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

# 8. LRU

class LRUCache {
  constructor(limit) {
    this.limit = limit
    this.cache = new Map()
  }
  get(key) {
    if (!this.cache.has(key)) return undefined
    const value = this.cache.get(key)
    this.cache.delete(key)
    this.cache.set(key, value)
    return value
  }
  put(key, value) {
    if (this.cache.has(key)) this.cache.delete(key)
    else if (this.cache.size >= this.limit) {
      this.cache.delete(this.cache.keys().next().value)
    }
    this.cache.set(key, value)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 9. Vue

  • index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vue</title>
  </head>
  <body>
    <center>
      <div id="app"></div>
    </center>

    <script type="module">
      import init from './init.js'
      const vm = {
        data() {
          return {
            papa: 'Jack',
          }
        },
        computed: {
          creation({ papa }) {
            console.log('computed:', papa)
            return papa === 'Jack' ? 'Alibaba' : 'Tencent'
          },
        },
        watch: {
          papa(value, oldValue) {
            console.log('watch:', value, oldValue)
          },
        },
      }

      init(vm)

      app.onclick = () => {
        vm.papa = vm.papa === 'Jack' ? 'Pony' : 'Jack'
      }
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  • init.js
import { initState } from './state.js'
import { mountComponent } from './lifecycle.js'

export default function init(vm) {
  // 初始化状态
  initState(vm)
  // 挂载组件
  mountComponent(vm)
}
1
2
3
4
5
6
7
8
9
  • state.js
import { observe } from './observe.js'
import Dep from './dep.js'
import Watcher from './watcher.js'
import { noop } from './util.js'

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop,
}

// 代理(主要用来代理到实例上)
function proxy(target, sourceKey, key) {
  sharedPropertyDefinition.get = function proxyGetter() {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter(val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

// 初始化状态
export function initState(vm) {
  if (vm.data) initData(vm)
  if (vm.computed) initComputed(vm, vm.computed)
  if (vm.watch) initWatch(vm, vm.watch)
}

// 初始化 data
function initData(vm) {
  vm._watchers = []
  let data = vm.data
  data = vm._data = typeof data === 'function' ? vm.data() : data || {}

  // 将数据代理到实例上
  const keys = Object.keys(data)
  let i = keys.length
  while (i--) {
    const key = keys[i]
    proxy(vm, `_data`, key)
  }

  observe(data)
}

// 初始化 computed
function initComputed(vm, computed) {
  const watchers = (vm._computedWatchers = Object.create(null))
  for (const key in computed) {
    const getter = computed[key]
    watchers[key] = new Watcher(vm, getter, noop, { lazy: true })
    // 将数据代理到实例上
    if (!(key in vm)) {
      defineComputed(vm, key)
    }
  }
}

// 定义响应式 computed
function defineComputed(target, key) {
  // 简化下
  Object.defineProperty(target, key, {
    enumerable: true,
    configurable: true,
    get() {
      const watcher = this._computedWatchers && this._computedWatchers[key]
      if (watcher) {
        // dirty 为 true 才会重新计算
        if (watcher.dirty) {
          watcher.evaluate()
        }
        // 收集依赖
        if (Dep.target) {
          watcher.depend()
        }
        return watcher.value
      }
    },
    set: noop,
  })
}

// 初始化 watch
function initWatch(vm, watch) {
  for (const key in watch) {
    const handler = watch[key]
    // 简化下 createWatcher 与 $watch
    new Watcher(vm, key, handler, { user: true })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
  • lifecycle.js
import { noop } from './util.js'
import Watcher from './watcher.js'

// 挂载组件
export function mountComponent(vm) {
  // 更新组件(Vue 中使用 path 函数,其中有 vnode、diff、ast...,这里简化)
  function updateComponent() {
    app.innerHTML = `${vm.papa} - ${vm.creation} - ${vm.creation}`
  }

  // RenderWatcher
  new Watcher(vm, updateComponent, noop, {}, true)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • observe.js
import Dep from './dep.js'
import { isObject } from './util.js'

class Observer {
  value
  dep
  constructor(value) {
    this.value = value
    this.dep = new Dep()
    Object.defineProperty(value, '__ob__', {
      value: this,
      enumerable: false,
      writable: true,
      configurable: true,
    })
    this.walk(value)
  }
  walk(obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
}

export function observe(value) {
  // 非对象不定义
  if (!isObject(value)) {
    return
  }
  new Observer(value)
}

// 定义为响应式数据
export function defineReactive(obj, key, val) {
  const dep = new Dep()
  val = obj[key]

  let childOb = observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      // 收集依赖
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
        }
      }
      return val
    },
    set(newVal) {
      if (newVal === val || (newVal !== newVal && val !== val)) {
        return
      }
      val = newVal
      // 定义新值为响应式对象
      childOb = observe(newVal)
      // 派发更新
      dep.notify()
    },
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  • dep.js
let uid = 0
export default class Dep {
  id
  subs
  constructor() {
    this.id = ++uid
    // Vue 在 watcher.addDep 中实现去重,这里简化下
    this.subs = new Set()
  }
  // 添加依赖
  addSub(sub) {
    this.subs.add(sub)
  }
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }
  // 通知依赖更新
  notify() {
    this.subs.forEach(sub => sub.update())
  }
}

Dep.target = null // 正在运行的 watcher
const targetStack = [] // watcher 执行栈

// 压栈
export function pushTarget(target) {
  targetStack.push(target)
  Dep.target = target
}

// 出栈
export function popTarget() {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  • watcher.js
import { pushTarget, popTarget } from './dep.js'

let uid = 0

export default class Watcher {
  vm
  cb
  id
  user
  value
  lazy
  dirty
  deps
  constructor(vm, expOrFn, cb, options, isRenderWatcher) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)

    // 主要为 watch 的回调
    this.cb = cb
    this.id = ++uid

    // watch 标识(多了错误提示)
    this.user = !!options.user
    // computed 标识
    this.lazy = !!options.lazy
    this.dirty = this.lazy

    this.deps = []
    // 字符串时(watch),获取值触发依赖收集,这里简化下
    this.getter = typeof expOrFn === 'function' ? expOrFn : vm => vm[expOrFn]
    // computed 通过 evaluate 计算值
    this.value = this.lazy ? undefined : this.get()
  }
  // 求值及重新收集依赖
  get() {
    pushTarget(this)
    let value
    const vm = this.vm
    // 所以 computed 的参数是组件实例
    value = this.getter.call(vm, vm)
    popTarget()
    return value
  }
  // 添加订阅
  addDep(dep) {
    dep.addSub(this)
  }
  // 依赖变化执行
  update() {
    if (this.lazy) {
      // computed 依赖变化,将 dirty = true 来重新计算值
      this.dirty = true
    } else {
      // 简化下 nextTick
      Promise.resolve().then(() => this.run())
    }
  }
  run() {
    const value = this.get()
    const oldValue = this.value
    this.value = value
    // 所以 watch 的回调可以获取新旧值
    this.cb.call(this.vm, value, oldValue)
  }
  // computed 计算值
  evaluate() {
    this.value = this.get()
    this.dirty = false
  }
  // computed 添加订阅
  depend() {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  • util.js
// 判断是否为对象
export function isObject(obj) {
  return obj !== null && typeof obj === 'object'
}

// 空函数
export function noop() {}
1
2
3
4
5
6
7

# 10. React

// 创建虚拟DOM
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: [],
    },
  }
}

// 创建真实DOM
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')
// 是否为除children和事件外的属性
const isProperty = key => key !== 'children' && !isEvent(key)
// 是否为新增属性
const isNew = (prev, next) => key => prev[key] !== next[key]
// 是否为移除属性
const isGone = (prev, next) => key => !(key in next)

// 更新真实DOM
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])
    })

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

  // 添加/更新属性
  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])
    })
}

// commit阶段
function commitRoot() {
  // 删除收集的旧节点
  deletions.forEach(commitWork)
  // 提交挂载节点
  commitWork(wipRoot.child)
  // 保存最后提交的fiber树
  currentRoot = wipRoot
  // 置空工作中的fiber树
  wipRoot = null
}

function commitWork(fiber) {
  if (!fiber) {
    return
  }

  let domParentFiber = fiber.parent
  // 向上查找有节点的fiber(处理函数组件)
  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) {
  // 向下查找有节点的fiber(处理函数组件)
  if (fiber.dom) {
    domParent.removeChild(fiber.dom)
  } else {
    commitDeletion(fiber.child, domParent)
  }
}

// Renderer(渲染器)
function render(element, container) {
  wipRoot = {
    dom: container,
    props: {
      children: [element],
    },
    alternate: currentRoot,
  }
  deletions = []
  nextUnitOfWork = wipRoot
}

// 下一个工作单元
let nextUnitOfWork = null
// 最后提交的fiber树
let currentRoot = null
// 工作中的fiber树
let wipRoot = null
// 要删除的旧节点
let deletions = null

// Scheduler(调度器)
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)
  }
  // 深度优先遍历
  if (fiber.child) {
    return fiber.child
  }
  let nextFiber = fiber
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }
    nextFiber = nextFiber.parent
  }
}

// 保存当前fiber
let wipFiber = null
// 记录hooks执行索引
let hookIndex = null

// 更新函数组件
function updateFunctionComponent(fiber) {
  wipFiber = fiber
  hookIndex = 0
  wipFiber.hooks = []
  // 执行函数获取节点
  const children = [fiber.type(fiber.props)]
  reconcileChildren(fiber, children)
}

// Hooks
function useState(initial) {
  const oldHook =
    wipFiber.alternate &&
    wipFiber.alternate.hooks &&
    wipFiber.alternate.hooks[hookIndex]
  const hook = {
    // 有旧hook使用旧状态,没有就用初始状态
    state: oldHook ? oldHook.state : initial,
    // hook更新队列
    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)
}

// Reconciler(协调器)
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',
      }
    }
    // 类型不同,并且有旧的fiber,则删除旧节点
    if (oldFiber && !sameType) {
      oldFiber.effectTag = 'DELETION'
      // 收集要删除的旧节点
      deletions.push(oldFiber)
    }

    // 比较兄弟fiber
    if (oldFiber) {
      oldFiber = oldFiber.sibling
    }

    // 第一个子元素作为child,其余的子元素作为sibling
    if (index === 0) {
      wipFiber.child = newFiber
    } else if (element) {
      prevSibling.sibling = newFiber
    }

    prevSibling = newFiber
    index++
  }
}

const Didact = {
  createElement,
  render,
  useState,
}

export default Didact
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/** @jsxRuntime classic @jsx Didact.createElement */
import Didact from './Didact.js'

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)
1
2
3
4
5
6
7
8
9
10
以父之名
周杰伦.mp3