h.js
-
-
function render(vnode) { // Strings just convert to #text Nodes: if (vnode.split) return document.createTextNode(vnode); // create a DOM element with the nodeName of our VDOM element: let n = document.createElement(vnode.nodeName); // copy attributes onto the new node: let a = vnode.attributes || {}; Object.keys(a).forEach( k => n.setAttribute(k, a[k]) ); // render (build) and then append child nodes: (vnode.children || []).forEach( c => n.appendChild(render(c)) ); return n; } var vnode = { nodeName: "div", attributes: { "id": "fei" }, children: ["Hello!"] } render(vnode) =======> <div id="fei">Hello!</div>
-
-
Creates a VNode (virtual DOM element). A tree of VNodes can be used as a lightweight representation
of the structure of a DOM tree. This structure can be realized by recursively comparing it against
the current actual DOM structure, and applying only the differences.
-
h(nodeName, attributes)
-
接受 nodeName 和 attributes,剩下参数将 push 到 stack 中
-
const stack = [] for (i = arguments.length; i-- > 2; ) { stack.push(arguments[i]); } -
nodeName: html 标签. 如:
div,a,span…..
-
constants.js
-
// render modes // 不 render export const NO_RENDER = 0; // 同步 render export const SYNC_RENDER = 1; // 强制 render export const FORCE_RENDER = 2; // 异步 render export const ASYNC_RENDER = 3; export const ATTR_KEY = '__preactattr_'; -
component.js
-
export function Component(props, context) { // 脏数据,在调用 this.setState(), 标记 _dirty = true, 到每一个事件循环结束, 检查所有标记 _dirty 的 component 重新绘制。 this._dirty = true; this.context = context; this.props = props; this.state = this.state || {}; } -
setState(state, cb) : 用来更新组件的 state —> enqueueRender
-
forceUpdate(cb): 立刻同步重新渲染组件 —> renderComponent
-
render: 返回组件的渲染内容的 vNode
dom / index.js
- createNode: document.createElement(nodeName)
- removeNode: parentNode.removeChild(node)
- setAccessor 设置 node 属性
- className、key、ref、style、dangerouslySetInnerHTML。。。。
- onXxxx: node.addEventListener(name, eventProxy, useCapture) 钩子函数
vdom
index.js
- isSameNodeType(node, vnode, hydrating)
- isNamedNode(node, nodeName)
- getNodeProps(vnode)
component.js
- setComponentProps
- renderComponent
- component.shouldComponentUpdate
- component.componentWillUpdate
- component.render
- component.componentDidUpdate
- options.afterUpdate
- component._renderCallbacks
- flushMounts()
- buildComponentFromVNode
- Apply the Component referenced by a VNode to the DOM.
- unmountComponent
- Remove a component from the DOM and recycle it
diff.js
-
Apply differences in a given vnode (and it's deep children) to a real DOM Node.
-
flushMounts()
-
diff(dom, vnode, context, mountAll, parent, componentRoot)
-
// dom: vnode 所对应的之前未更新的真实 dom // vnode: 要渲染的虚拟 dom // parent: 就是你要将虚拟dom挂载的父节点 export function diff(dom, vnode, context, mountAll, parent, componentRoot) { // diffLevel having been 0 here indicates initial entry into the diff (not a subdiff) if (!diffLevel++) { // when first starting the diff, check if we're diffing an SVG or within an SVG isSvgMode = parent!=null && parent.ownerSVGElement!==undefined; // hydration is indicated by the existing element to be diffed not having a prop cache hydrating = dom!=null && !(ATTR_KEY in dom); } let ret = idiff(dom, vnode, context, mountAll, componentRoot); // append the element if its a new parent if (parent && ret.parentNode!==parent) parent.appendChild(ret); // diffLevel being reduced to 0 means we're exiting the diff if (!--diffLevel) { hydrating = false; // invoke queued componentDidMount lifecycle methods if (!componentRoot) flushMounts(); } return ret; } -
idiff(dom, vnode, context, mountAll, componentRoot)
-
diff 算法的内部实现
-
function idiff(dom, vnode, context, mountAll, componentRoot) { // 空的node 渲染空的文本节点 if (vnode==null || typeof vnode==='boolean') vnode = ''; // VNode 为 String、Number 等简单类型类型节点 if(dom === 'text') { // dom 为 text, update if it's already a Text node: dom.nodeValue = vnode; } else { // dom 不是 text, replace it with one and recycle the old Element document.createTextNode(vnode); dom.parentNode.replaceChild(out, dom); recollectNodeTree(dom, true); } // VNode 为 component // dom 不存在或者类型错误 // dom 和 vnode name 不同 if (!dom || !isNamedNode(dom, vnodeName)) { out = createNode(vnodeName, isSvgMode); if (dom) { // move children into the replacement node while (dom.firstChild) out.appendChild(dom.firstChild); // if the previous Element was mounted into the DOM, replace it inline if (dom.parentNode) dom.parentNode.replaceChild(out, dom); // recycle the old element (skips non-Element node types) recollectNodeTree(dom, true); } } // dom 存在,并且和 vnode name 相同 let fc = out.firstChild, props = out[ATTR_KEY], vchildren = vnode.children; if (props == null) { props = out[ATTR_KEY] = {}; for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value; } ... // vchildren 只有一个直接赋值 if(vchildren.length===1) fc.nodeValue = vchildren[0] // else innerDiffNode() } // 重点,多看几遍这 function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) { let originalChildren = dom.childNodes, children = [], keyed = {}, keyedLen = 0, min = 0, len = originalChildren.length, childrenLen = 0, vlen = vchildren ? vchildren.length : 0, j, c, f, vchild, child; // 创建一个包含 key 的子元素和一个不包含有子元素的 Map /* 1. 遍历父 dom 节点,如果是 preact 所渲染并且有 key,keyed[key] = child; 2. isHydrating 为 true 时表示的是 dom 元素不是 Preact 创建的 child.nodeValue.trim() 3. dom 由 preact 创建children[childrenLen++] = child */ if (len!==0) { for (let i=0; i<len; i++) { let child = originalChildren[i], props = child[ATTR_KEY], key = vlen && props ? child._component ? child._component.__key : props.key : null; if (key!=null) { keyedLen++; keyed[key] = child; } else if (props || (child.splitText!==undefined ? (isHydrating ? child.nodeValue.trim() : true) : isHydrating)) { children[childrenLen++] = child; } } } // 遍历 vnode if (vlen!==0) { for (let i=0; i<vlen; i++) { vchild = vchildren[i]; child = null; // 通过 key 去寻找节点 let key = vchild.key; // 有 key if (key!=null) { if (keyedLen && keyed[key]!==undefined) { // 在 keyed 中查找对应的 dom元素,并在keyed将该元素删除 child = keyed[key]; keyed[key] = undefined; keyedLen--; } } // 从现有的子节点中找出相同类型的节点 else if (!child && min<childrenLen) { for (j=min; j<childrenLen; j++) { // isSameNodeType 查找是否和该元素类型相同的节点 if (children[j]!==undefined && isSameNodeType(c = children[j], vchild, isHydrating)) { // 有相同类型的节点,则在 children 中删除 child = c; children[j] = undefined; // 缩小排查范围 if (j===childrenLen-1) childrenLen--; if (j===min) min++; break; } } } // 递归 idiff // morph the matched/found/created DOM child to match vchild (deep) child = idiff(child, vchild, context, mountAll); f = originalChildren[i]; // 该 dom 与原始 dom 中对应位置的 dom if (child && child!==dom && child!==f) { if (f==null) { // 添加到对应位置之前 dom.appendChild(child); } else if (child===f.nextSibling) { // 移除当前的真实 dom removeNode(f); } else { // 添加到父节点 dom.insertBefore(child, f); } } } } -
// dom 事件触发 function diffAttributes(dom, attrs, old) { let name; // = undefined,移除不在 vnode 中的属性,这样都行啊 for (name in old) { if (!(attrs && attrs[name]!=null) && old[name]!=null) { setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode); } } // add new & update changed attributes for (name in attrs) { if (name!=='children' && name!=='innerHTML' && (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name]))) { setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode); } }
-
component-recycler.js
-
const components = {}
- Retains a pool of Components for re-use, keyed on component name.
-
collectComponent(component)
- Reclaim a component for later re-use by the recycler
- component.constructor.name;
-
createComponent(Ctor, props, context)
-
创建组件实例(PFC's and classful Components)
-
RFC(Pure Function Component) 纯函数组件
-
// 从创建的池中取出同类组件的实例,再取出该实例之前渲染的实例(nextBase) // 目的: 只渲染当前 dom,优化渲染 if (list) { for (let i=list.length; i--; ) { if (list[i].constructor===Ctor) { // 赋值到我们的新创建组件实例的 nextBase 属性上 inst.nextBase = list[i].nextBase; list.splice(i, 1); break; } } }
-