Skip to content

Instantly share code, notes, and snippets.

@marvinhagemeister
Created March 8, 2020 14:13
Show Gist options
  • Star 51 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save marvinhagemeister/8950b1032d67918d21950b3985259d78 to your computer and use it in GitHub Desktop.
Save marvinhagemeister/8950b1032d67918d21950b3985259d78 to your computer and use it in GitHub Desktop.
Jason little-vdom decompiled
/* eslint-disable no-unused-vars */
/* eslint-disable no-else-return */
// JSX constructor, similar to createElement()
export const h = (type, props, ...children) => {
return {
type,
// Props will be an object for components and DOM nodes, but a string for
// text nodes
props,
children,
key: props && props.key
};
};
export const render = (
newVNode,
dom,
oldVNode = dom._vnode || (dom._vnode = {}),
currentChildIndex
) => {
// Check if we are in fact dealing with an array of nodes. A more common
// and faster version of this check is Array.isArray()
if (newVNode.pop) {
return newVNode.map((child, index) => {
render(
child,
dom,
oldVNode._normalizedChildren && oldVNode._normalizedChildren[index]
);
});
}
// Check if we have a component. Only functions have a .call() method.
// Here components have a different signature compared to Preact or React:
//
// (props, state, updateFn) => VNode;
//
// The 3rd argument is basically similar concept-wise to setState
else if (newVNode.type.call) {
// Initialize state of component if necessary
newVNode.state = oldVNode.state || {};
// Add children to props
const props = { children: newVNode.children, ...newVNode.props };
const renderResult = newVNode.type(
props,
newVNode.state,
// Updater function that is passed as 3rd argument to components
nextState => {
// Update state with new value
Object.assign(newVNode.state, nextState);
return render(newVNode, dom, newVNode);
}
);
return (newVNode.i = render(
renderResult,
oldVNode.i || dom,
(oldVNode && oldVNode.i) || {},
newVNode
));
}
// Standard DOM elements
else {
// Create a DOM element and assign it to the vnode. If one already exists,
// we will reuse the existing one and not create a new node.
newVNode.dom =
oldVNode.dom ||
(newVNode.type
? document.createElement(newVNode.type)
: // If we have a text node, vnode.props will be a string
new Text(newVNode.props));
// diff props
if (newVNode.props != oldVNode.props) {
// If newVNode.type is truthy (=not an empty string) we have a DOM node
if (newVNode.type) {
Object.keys(newVNode.props || {}).map(name => {
let value = newVNode.props[name];
if (
value != (oldVNode.props && oldVNode.props[name]) &&
name in newVNode.dom
) {
return (newVNode.dom[name] = value);
} else {
return newVNode.dom.setAttribute(name, value);
}
});
}
// Otherwise a text node
else {
// Update text node content
newVNode.dom.data = newVNode.props;
}
}
// insert at position
if (oldVNode.dom || currentChildIndex != undefined) {
dom.insertBefore(newVNode.dom, dom.childNodes[currentChildIndex + 1]);
}
// diff children (typed/keyed)
newVNode._normalizedChildren = newVNode.children.concat
.apply([], newVNode.children)
.map((child, index) => {
// If we have previous children we search for one that matches our
// current vnode
const nextOldChild = oldVNode._normalizedChildren
? oldVNode._normalizedChildren.find((oldChild, childIndex) => {
return (
oldChild &&
oldChild.type == child.type &&
oldChild.key == child.key &&
(childIndex == index && (index = undefined),
(oldVNode._normalizedChildren[childIndex] = 0),
oldChild)
);
})
: {};
// Continue diffing recursively against the next child. If the vnode
// has no children we assume that we have a string and convert it into
// a text vnode.
const nextNewChild = child.children || h('', child);
render(nextNewChild, newVNode.dom, nextOldChild, index);
});
// remove old children if there are any
if (oldVNode._normalizedChildren) {
oldVNode._normalizedChildren.map(oldChild => {
oldChild && oldChild.dom.remove();
});
}
Object.assign(oldVNode, newVNode);
}
};
@marvinhagemeister
Copy link
Author

@AjayPoshak Take a look at the h() function above. That's function that needs to be called by the JSX compiler. Typically the compiler (babel or TypeScript) have a JSX pragma setting where you can specify the JSX constructor function. Pointing that to h should do the job 👍

@luwes
Copy link

luwes commented Jan 14, 2022

thank you! child.children || h('', child); should be child.children ? child : h('', child);
I fixed some other small bugs here https://github.com/luwes/little-vdom/blob/main/little-vdom.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment