Skip to content

Commit

Permalink
Merge b8bd7b4 into eec1beb
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewiggins committed Apr 6, 2019
2 parents eec1beb + b8bd7b4 commit 9972fdd
Show file tree
Hide file tree
Showing 5 changed files with 378 additions and 108 deletions.
2 changes: 1 addition & 1 deletion src/component.js
Expand Up @@ -70,7 +70,7 @@ Component.prototype.forceUpdate = function(callback) {
const force = callback!==false;

let mounts = [];
dom = diff(dom, parentDom, vnode, vnode, this._context, parentDom.ownerSVGElement!==undefined, null, mounts, this._ancestorComponent, force);
dom = diff(dom, parentDom, vnode, vnode, this._context, parentDom.ownerSVGElement!==undefined, null, mounts, this._ancestorComponent, force, dom);
if (dom!=null && dom.parentNode!==parentDom) {
parentDom.appendChild(dom);
}
Expand Down
27 changes: 7 additions & 20 deletions src/diff/children.js
Expand Up @@ -18,33 +18,20 @@ import { removeNode } from '../util';
* which have mounted
* @param {import('../internal').Component} ancestorComponent The direct parent
* component to the ones being diffed
* @param {Node | Text} childDom The current attached DOM
* element any new dom elements should be placed around. Likely `null` on first
* render (except when hydrating). Can be a sibling DOM element when diffing
* Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
*/
export function diffChildren(parentDom, newParentVNode, oldParentVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent) {
export function diffChildren(parentDom, newParentVNode, oldParentVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, childDom) {
let childVNode, i, j, p, index, oldVNode, newDom,
nextDom, sibDom, focus,
childDom;
nextDom, sibDom, focus;

let newChildren = newParentVNode._children || toChildArray(newParentVNode.props.children, newParentVNode._children=[], coerceToVNode);
let oldChildren = oldParentVNode!=null && oldParentVNode!=EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR;

let oldChildrenLength = oldChildren.length;

for (i = 0; i < oldChildrenLength; i++) {
if (oldChildren[i] && oldChildren[i]._dom) {
childDom = oldChildren[i]._dom;
break;
}
}

if (excessDomChildren!=null) {
for (i = 0; i < excessDomChildren.length; i++) {
if (excessDomChildren[i]!=null) {
childDom = excessDomChildren[i];
break;
}
}
}

for (i=0; i<newChildren.length; i++) {
childVNode = newChildren[i] = coerceToVNode(newChildren[i]);
oldVNode = index = null;
Expand Down Expand Up @@ -78,7 +65,7 @@ export function diffChildren(parentDom, newParentVNode, oldParentVNode, context,
nextDom = childDom!=null && childDom.nextSibling;

// Morph the old element into the new one, but don't append it to the dom yet
newDom = diff(oldVNode==null ? null : oldVNode._dom, parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, null);
newDom = diff(oldVNode==null ? null : oldVNode._dom, parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, null, childDom);

// Only proceed if the vnode has not been unmounted by `diff()` above.
if (childVNode!=null && newDom !=null) {
Expand Down
53 changes: 47 additions & 6 deletions src/diff/index.js
Expand Up @@ -20,8 +20,12 @@ import options from '../options';
* mounted components
* @param {import('../internal').Component | null} ancestorComponent The direct
* parent component
* @param {Node | Text} childDom The current attached DOM
* element any new dom elements should be placed around. Likely `null` on first
* render (except when hydrating). Can be a sibling DOM element when diffing
* Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
*/
export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, force) {
export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, force, childDom) {

// If the previous type doesn't match the new type we drop the whole subtree
if (oldVNode==null || newVNode==null || oldVNode.type!==newVNode.type) {
Expand All @@ -41,15 +45,18 @@ export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessD

try {
outer: if (oldVNode.type===Fragment || newType===Fragment) {
diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, c);
diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, c, childDom);

// Mark dom as empty in case `_children` is any empty array. If it isn't
// we'll set `dom` to the correct value just a few lines later.
dom = null;

if (newVNode._children.length) {
dom = newVNode._children[0]._dom;
newVNode._lastDomChild = newVNode._children[newVNode._children.length - 1]._dom;

// If lastChild is a Fragment, use _lastDomChild, else use _dom
let lastChild = newVNode._children[newVNode._children.length - 1];
newVNode._lastDomChild = lastChild._lastDomChild || lastChild._dom;
}
}
else if (typeof newType==='function') {
Expand Down Expand Up @@ -113,6 +120,7 @@ export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessD
c.props = newVNode.props;
c.state = s;
c._dirty = false;
newVNode._lastDomChild = oldVNode._lastDomChild;
break outer;
}

Expand Down Expand Up @@ -142,7 +150,7 @@ export function diff(dom, parentDom, newVNode, oldVNode, context, isSvg, excessD
snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
}

c.base = dom = diff(dom, parentDom, vnode, prev, context, isSvg, excessDomChildren, mounts, c, null);
c.base = dom = diff(dom, parentDom, vnode, prev, context, isSvg, excessDomChildren, mounts, c, null, childDom);

if (vnode!=null) {
// If this component returns a Fragment (or another component that
Expand Down Expand Up @@ -277,7 +285,9 @@ function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChil
if (newProps.multiple) {
dom.multiple = newProps.multiple;
}
diffChildren(dom, newVNode, oldVNode, context, newVNode.type==='foreignObject' ? false : isSvg, excessDomChildren, mounts, ancestorComponent);

const childDom = getFirstChildDom(oldVNode, excessDomChildren);
diffChildren(dom, newVNode, oldVNode, context, newVNode.type==='foreignObject' ? false : isSvg, excessDomChildren, mounts, ancestorComponent, childDom);
diffProps(dom, newProps, oldProps, isSvg);
}
}
Expand Down Expand Up @@ -305,7 +315,7 @@ export function applyRef(ref, value, ancestorComponent) {
* @param {import('../internal').VNode} vnode The virtual node to unmount
* @param {import('../internal').Component} ancestorComponent The parent
* component to this virtual node
* @param {boolean} skipRemove Flag that indicates that a parent node of the
* @param {boolean} [skipRemove] Flag that indicates that a parent node of the
* current element is already detached from the DOM.
*/
export function unmount(vnode, ancestorComponent, skipRemove) {
Expand Down Expand Up @@ -378,3 +388,34 @@ function catchErrorInComponent(error, component) {
}
throw error;
}

/**
* Determine which currently attached DOM node to use when beginning to
* diff the children of a VNode
* @param {import('../internal').VNode} oldVNode The old VNode whose children
* are about to be diffed
* @param {import('../internal').PreactElement[]} excessDomChildren If hydrating,
* the currently attached DOM elements that are being hydrated
* @returns {import('../internal').PreactElement | Text | undefined}
*/
export function getFirstChildDom(oldVNode, excessDomChildren) {

/** @type {import('../internal').VNode[]} */
let oldChildren = oldVNode!=null && oldVNode!=EMPTY_OBJ && oldVNode._children || EMPTY_ARR;

let i;
if (excessDomChildren!=null) {
for (i = 0; i < excessDomChildren.length; i++) {
if (excessDomChildren[i]!=null) {
return excessDomChildren[i];
}
}
}
else {
for (i = 0; i < oldChildren.length; i++) {
if (oldChildren[i] && oldChildren[i]._dom) {
return oldChildren[i]._dom;
}
}
}
}
7 changes: 5 additions & 2 deletions src/render.js
@@ -1,5 +1,5 @@
import { EMPTY_OBJ, EMPTY_ARR } from './constants';
import { commitRoot } from './diff/index';
import { commitRoot, getFirstChildDom } from './diff/index';
import { diffChildren } from './diff/children';
import { createElement, Fragment } from './create-element';
import options from './options';
Expand All @@ -15,8 +15,11 @@ export function render(vnode, parentDom) {
let oldVNode = parentDom._prevVNode;
vnode = createElement(Fragment, null, [vnode]);

const excessDomChildren = oldVNode ? null : EMPTY_ARR.slice.call(parentDom.childNodes);
const childDom = getFirstChildDom(oldVNode, excessDomChildren);

let mounts = [];
diffChildren(parentDom, parentDom._prevVNode = vnode, oldVNode, EMPTY_OBJ, parentDom.ownerSVGElement!==undefined, oldVNode ? null : EMPTY_ARR.slice.call(parentDom.childNodes), mounts, vnode);
diffChildren(parentDom, parentDom._prevVNode = vnode, oldVNode, EMPTY_OBJ, parentDom.ownerSVGElement!==undefined, excessDomChildren, mounts, vnode, childDom);
commitRoot(mounts, vnode);
}

Expand Down

0 comments on commit 9972fdd

Please sign in to comment.