h.js
-
wtf-is-jsx
-
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> Copy the code
-
-
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)
-
Accept nodeName and Attributes, and the remaining parameters are pushed onto the stack
-
const stack = [] for (i = arguments.length; i-- > 2;) { stack.push(arguments[i]); } Copy the code
-
NodeName: indicates the HTML tag. Div, a, span… .
-
constants.js
-
// render modes / / not render export const NO_RENDER = 0; Render / / synchronization export const SYNC_RENDER = 1; / / render export const FORCE_RENDER = 2; Asynchronous render / / export const ASYNC_RENDER = 3; export const ATTR_KEY = '__preactattr_'; Copy the code
-
component.js
-
export function Component(props, context) { After calling this.setstate (), mark _dirty = true. At the end of each event loop, check that all components marked _dirty are redrawn. this._dirty = true; this.context = context; this.props = props; this.state = this.state || {}; } Copy the code
-
SetState (state, cb) : State – > enqueueRender used to update components
-
ForceUpdate (CB): Rerender components immediately — > renderComponent
-
Render: vNode that returns the render content of the component
dom / index.js
- createNode: document.createElement(nodeName)
- removeNode: parentNode.removeChild(node)
- SetAccessor Sets node properties
- ClassName, key, ref, style, dangerouslySetInnerHTML…
- OnXxxx: Node. addEventListener(Name, eventProxy, useCapture) hook function
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: the previously unupdated real DOM corresponding to the vNode // vnode: virtual DOM to render // parent: the parent node to which you want to mount the virtual 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 SVGisSvgMode = parent! =null&& parent.ownerSVGElement! = =undefined; // hydration is indicated by the existing element to be diffed not having a prop cachehydrating = 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; } Copy the code
-
idiff(dom, vnode, context, mountAll, componentRoot)
-
Internal implementation of diff algorithm
-
function idiff(dom, vnode, context, mountAll, componentRoot) { // An empty node renders an empty text node if (vnode==null || typeof vnode==='boolean') vnode = ' '; // VNode is a node of simple types such as String and Number if(dom === 'text') { // dom is text, update if it's already a text node: dom.nodeValue = vnode; } else { // dom is not text, replace it with one and recycle the old Element document.createTextNode(vnode); dom.parentNode.replaceChild(out, dom); recollectNodeTree(dom, true); } / / VNode for component // DOM does not exist or has an incorrect type // Dom and vnode names are different 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 exists and has the same name as vnode let fc = out.firstChild, props = out[ATTR_KEY], vchildren = vnode.children; if (props == null) { props = out[ATTR_KEY] = {}; for (leta=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value; }...// Vchildren has only one direct assignment if(vchildren.length===1) fc.nodeValue = vchildren[0] // else innerDiffNode() } // Important, watch this a few times 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; // Create a child element with key and a Map without child elements Keyed [key] = child; /* preact [key] = child; Child.nodevalue.trim (); 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; }}}/ / traverse vnode if(vlen! = =0) { for (let i=0; i<vlen; i++) { vchild = vchildren[i]; child = null; // Use key to find nodes let key = vchild.key; / / have the key if(key! =null) { if(keyedLen && keyed[key]! = =undefined) { // Find the dom element in keyed and delete the element in keyed child = keyed[key]; keyed[key] = undefined; keyedLen--; }}// Find nodes of the same type from existing child nodes else if(! child && min<childrenLen) {for (j=min; j<childrenLen; j++) { // isSameNodeType looks for nodes of the same type as the element if(children[j]! = =undefined && isSameNodeType(c = children[j], vchild, isHydrating)) { // If there are nodes of the same type, delete them in children child = c; children[j] = undefined; // Narrow down the search if (j===childrenLen- 1) childrenLen--; if (j===min) min++; break; }}}/ / recursive idiff // morph the matched/found/created DOM child to match vchild (deep) child = idiff(child, vchild, context, mountAll); f = originalChildren[i]; // This dom corresponds to the corresponding DOM in the original DOM if(child && child! ==dom && child! ==f) {if (f==null) { // Add it before the corresponding location dom.appendChild(child); } else if (child===f.nextSibling) { // Remove the current real DOM removeNode(f); } else { // Add to the parent nodedom.insertBefore(child, f); }}}}Copy the code
-
// DOM event is triggered function diffAttributes(dom, attrs, old) { let name; // = undefined, remove attributes that are not in 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'&& (! (nameinold) || attrs[name]! ==(name==='value' || name==='checked'? dom[name] : old[name]))) { setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode); }}Copy the code
-
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)
-
Creating component instances (PFC’s and Classful Components)
-
Pure Function Component (RFC) Pure Function Component
-
// Fetch the instance of the same class from the pool created, and then fetch the instance rendered before that instance (nextBase) // Purpose: Render only the current DOM, optimize rendering if (list) { for (let i=list.length; i--; ) { if (list[i].constructor===Ctor) { // Assign to the nextBase property of our newly created component instance inst.nextBase = list[i].nextBase; list.splice(i, 1); break; }}}Copy the code
-