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
| import { updateDomProperties } from "./dom-utils.js"
let rootInstance = null const TEXT_ELEMENT = "TEXT_ELEMENT"
function createElement(type, config, ...args) { const props = Object.assign({}, config) const hasChildren = args.length > 0 const rawChildren = hasChildren ? [].concat(...args) : []
props.children = rawChildren .filter((c) => c !== null && c !== false) .map((c) => (c instanceof Object ? c : createTextElement(c)))
return { type, props } }
function createTextElement(text) { return createElement(TEXT_ELEMENT, { nodeValue: text }) }
function render(element, container) { const prevInstance = rootInstance const nextInstance = reconcile(container, prevInstance, element) rootInstance = nextInstance }
function reconcile(parentDom, instance, element) { if (instance === null) { const newInstance = instantiate(element) parentDom.appendChild(newInstance.dom) return newInstance } else if (element === null) { parentDom.removeChild(instance.dom) return null } else if (instance.element.type !== element.type) { const newInstance = instantiate(element) parentDom.replaceChild(newInstance.dom, instance.dom) return newInstance } else if (typeof element.type === "string") { updateDomProperties(instance.dom, instance.element.props, element.props)
instance.childInstances = reconcilerChildren(instance, element) instance.element = element
return instance } else { instance.publicInstance.props = element.props const childElement = instance.publicInstance.render() const oldChildInstance = instance.childInstance const childInstance = reconcile(parentDom, oldChildInstance, childElement)
instance.dom = childInstance.dom instance.childInstance = childInstance instance.element = element
return instance } }
function reconcilerChildren(instance, element) { const dom = instance.dom const childInstances = instance.childInstances const nextChildElements = element.props.children || [] const newChildInstances = []
const count = Math.max(childInstances.length, nextChildElements.length)
for (let i = 0; i < count; i++) { const childInstance = childInstances[i] const childElement = nextChildElements[i]
const newChildInstance = reconcile(dom, childInstance, childElement)
newChildInstances.push(newChildInstance) }
return newChildInstances.filter((instance) => instance !== null) }
function instantiate(element) { const { type, props } = element const isDomElement = typeof type === "string"
if (isDomElement) { const isTextElement = type === TEXT_ELEMENT const dom = isTextElement ? document.createTextNode("") : document.createElement(type)
updateDomProperties(dom, [], props)
const childElements = props.children || [] const childInstances = childElements.map(instantiate) const childDoms = childInstances.map((childInstance) => childInstance.dom) childDoms.forEach((childDom) => dom.appendChild(childDom))
const instance = { dom, element, childInstances }
return instance } else { const instance = {} const publicInstance = createPublicInstance(element, instance) const childElement = publicInstance.render() const childInstance = instantiate(childElement) const dom = childInstance.dom
Object.assign(instance, { dom, element, publicInstance, childInstance })
return instance } }
function createPublicInstance(element, internalInstance) { const { type, props } = element
const publicInstance = new type(props)
publicInstance.__internalInstance = internalInstance
return publicInstance }
function updateInstance(internalInstance) { const parentDom = internalInstance.dom.parentNode const element = internalInstance.element
reconcile(parentDom, internalInstance, element) }
class Component { constructor(props) { this.props = props this.state = this.state || {} }
setState(partialState) { this.state = Object.assign({}, this.state, partialState) updateInstance(this.__internalInstance) }
render() {} }
export default { render, createElement, Component }
|