手搓螺旋丸
coderljw 2024-10-13 大约 10 分钟
# 1. Object.create
- 2006 年,Douglas Crockford 写了一篇文章:《JavaScript 中的原型式继承》 (opens new window)。
function object(o) {
function F() {}
F.prototype = o
return new F()
}
1
2
3
4
5
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
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
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
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
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
66JSON.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
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
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10