1. function createElement(type, props, ...children) {
    2. return {
    3. type,
    4. props: {
    5. ...props,
    6. children: children.map(child =>
    7. typeof child === "object"
    8. ? child
    9. : createTextElement(child)
    10. ),
    11. },
    12. }
    13. }
    14. function createTextElement(text) {
    15. return {
    16. type: "TEXT_ELEMENT",
    17. props: {
    18. nodeValue: text,
    19. children: [],
    20. },
    21. }
    22. }
    23. function createDom(fiber) {
    24. // 判断dom 是否为文本节点
    25. const dom =
    26. fiber.type == "TEXT_ELEMENT"
    27. ? document.createTextNode("")
    28. : document.createElement(fiber.type)
    29. updateDom(dom, {}, fiber.props)
    30. return dom
    31. }
    32. const isEvent = key => key.startsWith("on")
    33. const isProperty = key =>
    34. key !== "children" && !isEvent(key)
    35. const isNew = (prev, next) => key =>
    36. prev[key] !== next[key]
    37. const isGone = (prev, next) => key => !(key in next)
    38. function updateDom(dom, prevProps, nextProps) {
    39. //Remove old or changed event listeners
    40. Object.keys(prevProps)
    41. .filter(isEvent)
    42. .filter(
    43. key =>
    44. !(key in nextProps) ||
    45. isNew(prevProps, nextProps)(key)
    46. )
    47. .forEach(name => {
    48. const eventType = name
    49. .toLowerCase()
    50. .substring(2) //从第三个字符串开始截取 onClick -> click
    51. // 移除事件
    52. dom.removeEventListener(
    53. eventType,
    54. prevProps[name]
    55. )
    56. })
    57. // Remove old properties
    58. Object.keys(prevProps)
    59. .filter(isProperty)
    60. .filter(isGone(prevProps, nextProps))
    61. .forEach(name => {
    62. dom[name] = ""
    63. })
    64. // Set new or changed properties
    65. Object.keys(nextProps)
    66. .filter(isProperty)
    67. .filter(isNew(prevProps, nextProps))
    68. .forEach(name => {
    69. dom[name] = nextProps[name]
    70. })
    71. // Add event listeners
    72. Object.keys(nextProps)
    73. .filter(isEvent)
    74. .filter(isNew(prevProps, nextProps))
    75. .forEach(name => {
    76. const eventType = name
    77. .toLowerCase()
    78. .substring(2)
    79. dom.addEventListener(
    80. eventType,
    81. nextProps[name]
    82. )
    83. })
    84. }
    85. function commitRoot() {
    86. deletions.forEach(commitWork)
    87. commitWork(wipRoot.child)
    88. currentRoot = wipRoot
    89. wipRoot = null
    90. }
    91. function commitWork(fiber) {
    92. if (!fiber) {
    93. return
    94. }
    95. let domParentFiber = fiber.parent
    96. while (!domParentFiber.dom) {
    97. domParentFiber = domParentFiber.parent
    98. }
    99. const domParent = domParentFiber.dom
    100. if (
    101. fiber.effectTag === "PLACEMENT" &&
    102. fiber.dom != null
    103. ) {
    104. domParent.appendChild(fiber.dom)
    105. } else if (
    106. fiber.effectTag === "UPDATE" &&
    107. fiber.dom != null
    108. ) {
    109. updateDom(
    110. fiber.dom,
    111. fiber.alternate.props,
    112. fiber.props
    113. )
    114. } else if (fiber.effectTag === "DELETION") {
    115. commitDeletion(fiber, domParent)
    116. }
    117. commitWork(fiber.child)
    118. commitWork(fiber.sibling)
    119. }
    120. function commitDeletion(fiber, domParent) {
    121. if (fiber.dom) {
    122. domParent.removeChild(fiber.dom)
    123. } else {
    124. commitDeletion(fiber.child, domParent)
    125. }
    126. }
    127. function render(element, container) {
    128. wipRoot = {
    129. dom: container,
    130. props: {
    131. children: [element],
    132. },
    133. alternate: currentRoot,
    134. }
    135. deletions = []
    136. nextUnitOfWork = wipRoot
    137. }
    138. let nextUnitOfWork = null
    139. let currentRoot = null
    140. let wipRoot = null
    141. let deletions = null
    142. // nextUnitOfWork 下一个需要处理的fiber节点
    143. function workLoop(deadline) {
    144. let shouldYield = false
    145. while (nextUnitOfWork && !shouldYield) {
    146. nextUnitOfWork = performUnitOfWork(
    147. nextUnitOfWork
    148. )
    149. shouldYield = deadline.timeRemaining() < 1
    150. }
    151. if (!nextUnitOfWork && wipRoot) {
    152. commitRoot()
    153. }
    154. requestIdleCallback(workLoop)
    155. }
    156. requestIdleCallback(workLoop)
    157. function performUnitOfWork(fiber) {
    158. // 判断是否是函数组件
    159. const isFunctionComponent =
    160. fiber.type instanceof Function
    161. if (isFunctionComponent) {
    162. updateFunctionComponent(fiber)
    163. } else {
    164. updateHostComponent(fiber)
    165. }
    166. // 如果这个fiber有子节点
    167. if (fiber.child) {
    168. return fiber.child
    169. }
    170. // 如果fiber没有子节点
    171. let nextFiber = fiber
    172. while (nextFiber) {
    173. // 判断是否有兄弟节点,如果有则返回兄弟节点
    174. if (nextFiber.sibling) {
    175. return nextFiber.sibling
    176. }
    177. //否则给 nextFiber 赋值为父节点,此时:while条件 判断父节点的兄弟节点
    178. nextFiber = nextFiber.parent
    179. }
    180. }
    181. // workInProgressFiber
    182. let wipFiber = null
    183. let hookIndex = null
    184. function updateFunctionComponent(fiber) {
    185. wipFiber = fiber // 默认把fiber给wipFiber
    186. hookIndex = 0 // 默认把 hookIndex 赋值为0
    187. wipFiber.hooks = []
    188. const children = [fiber.type(fiber.props)]
    189. reconcileChildren(fiber, children)
    190. }
    191. function useState(initial) {
    192. const oldHook =
    193. wipFiber.alternate &&
    194. wipFiber.alternate.hooks &&
    195. wipFiber.alternate.hooks[hookIndex]
    196. const hook = {
    197. state: oldHook ? oldHook.state : initial,
    198. queue: [],
    199. }
    200. const actions = oldHook ? oldHook.queue : []
    201. actions.forEach(action => {
    202. hook.state = action(hook.state)
    203. })
    204. const setState = action => {
    205. hook.queue.push(action)
    206. wipRoot = {
    207. dom: currentRoot.dom,
    208. props: currentRoot.props,
    209. alternate: currentRoot,
    210. }
    211. nextUnitOfWork = wipRoot
    212. deletions = []
    213. }
    214. wipFiber.hooks.push(hook)
    215. hookIndex++
    216. return [hook.state, setState]
    217. }
    218. function updateHostComponent(fiber) {
    219. if (!fiber.dom) {
    220. fiber.dom = createDom(fiber)
    221. }
    222. reconcileChildren(fiber, fiber.props.children)
    223. }
    224. function reconcileChildren(wipFiber, elements) {
    225. let index = 0
    226. let oldFiber =
    227. wipFiber.alternate && wipFiber.alternate.child
    228. let prevSibling = null
    229. while (
    230. index < elements.length ||
    231. oldFiber != null
    232. ) {
    233. const element = elements[index]
    234. let newFiber = null
    235. const sameType =
    236. oldFiber &&
    237. element &&
    238. element.type == oldFiber.type
    239. if (sameType) {
    240. newFiber = {
    241. type: oldFiber.type,
    242. props: element.props,
    243. dom: oldFiber.dom,
    244. parent: wipFiber,
    245. alternate: oldFiber,
    246. effectTag: "UPDATE", // 标记节点是需要更新
    247. }
    248. }
    249. if (element && !sameType) {
    250. newFiber = {
    251. type: element.type,
    252. props: element.props,
    253. dom: null,
    254. parent: wipFiber,
    255. alternate: null,
    256. effectTag: "PLACEMENT",
    257. }
    258. }
    259. if (oldFiber && !sameType) {
    260. oldFiber.effectTag = "DELETION"
    261. deletions.push(oldFiber)
    262. }
    263. if (oldFiber) {
    264. oldFiber = oldFiber.sibling
    265. }
    266. if (index === 0) {
    267. wipFiber.child = newFiber
    268. } else if (element) {
    269. prevSibling.sibling = newFiber
    270. }
    271. prevSibling = newFiber
    272. index++
    273. }
    274. }
    275. const Didact = {
    276. createElement,
    277. render,
    278. useState,
    279. }
    280. /** @jsx Didact.createElement */
    281. function Counter() {
    282. const [state, setState] = Didact.useState(1)
    283. return (
    284. <h1 onClick={() => setState(c => c + 1)}>
    285. Count: {state}
    286. </h1>
    287. )
    288. }
    289. const element = <Counter />
    290. const container = document.getElementById("root")
    291. Didact.render(element, container)