diff --git a/src/patch.ts b/src/patch.ts index 17d09cee04..859ccf61bc 100644 --- a/src/patch.ts +++ b/src/patch.ts @@ -191,82 +191,99 @@ export const flushWorkStack = ( }; /** - * Diffs two VNodes and modifies the DOM node based on the necessary changes + * Creates a custom patch function */ -export const patch = ( - el: HTMLElement | Text, - newVNode: VNode, - prevVNode?: VNode, - workStack: (() => void)[] = [], - commit?: (callback: () => void) => void, -): HTMLElement | Text => { - const finish = () => { - flushWorkStack(workStack, commit); - if (!prevVNode) el[OLD_VNODE_FIELD] = newVNode; - }; - - if (!newVNode) { - workStack.push(() => el.remove()); - finish(); - return el; - } else { - const oldVNode: VNode | undefined = prevVNode ?? el[OLD_VNODE_FIELD]; - const hasString = typeof oldVNode === 'string' || typeof newVNode === 'string'; +export const init = + ( + customPatchProps: typeof patchProps = patchProps, + customPatchChildren: typeof patchChildren = patchChildren, + ...effects: (() => void)[] + ) => + ( + el: HTMLElement | Text, + newVNode: VNode, + prevVNode?: VNode, + workStack: (() => void)[] = [], + commit?: (callback: () => void) => void, + ): HTMLElement | Text => { + const finish = () => { + flushWorkStack(workStack, commit); + if (!prevVNode) el[OLD_VNODE_FIELD] = newVNode; + }; - if (hasString && oldVNode !== newVNode) { - const newEl = createElement(newVNode); - workStack.push(() => el.replaceWith(newEl)); + if (!newVNode) { + workStack.push(() => el.remove()); finish(); - return newEl; - } else if (!hasString) { - if ( - (!(oldVNode)?.key && !(newVNode)?.key) || - (oldVNode)?.key !== (newVNode)?.key - ) { - if ((oldVNode)?.tag !== (newVNode)?.tag || el instanceof Text) { - const newEl = createElement(newVNode); - workStack.push(() => el.replaceWith(newEl)); - finish(); - return newEl; - } else { - patchProps( - el, - (oldVNode)?.props || {}, - (newVNode).props || {}, - workStack, - ); + return el; + } else { + const oldVNode: VNode | undefined = prevVNode ?? el[OLD_VNODE_FIELD]; + const hasString = typeof oldVNode === 'string' || typeof newVNode === 'string'; - // Flags allow for greater optimizability by reducing condition branches. - // Generally, you should use a compiler to generate these flags, but - // hand-writing them is also possible - switch ((newVNode).flag) { - case VFlags.NO_CHILDREN: - workStack.push(() => (el.textContent = '')); - break; - case VFlags.ONLY_TEXT_CHILDREN: - // Joining is faster than setting textContent to an array - workStack.push( - () => (el.textContent = (newVNode).children!.join('')), - ); - break; - default: - patchChildren( - el, - (oldVNode)?.children || [], - (newVNode).children!, - (newVNode).flag === VFlags.ONLY_KEYED_CHILDREN, - // We need to pass delta here because this function does not have - // a reference to the actual vnode. - (newVNode).delta, - workStack, - ); - break; + if (hasString && oldVNode !== newVNode) { + const newEl = createElement(newVNode); + workStack.push(() => el.replaceWith(newEl)); + finish(); + return newEl; + } else if (!hasString) { + if ( + (!(oldVNode)?.key && !(newVNode)?.key) || + (oldVNode)?.key !== (newVNode)?.key + ) { + if ((oldVNode)?.tag !== (newVNode)?.tag || el instanceof Text) { + const newEl = createElement(newVNode); + workStack.push(() => el.replaceWith(newEl)); + finish(); + return newEl; + } else { + customPatchProps( + el, + (oldVNode)?.props || {}, + (newVNode).props || {}, + workStack, + ); + + if (effects.length > 0) { + for (let i = 0; i < effects.length; ++i) { + effects[i](); + } + } + + // Flags allow for greater optimizability by reducing condition branches. + // Generally, you should use a compiler to generate these flags, but + // hand-writing them is also possible + switch ((newVNode).flag) { + case VFlags.NO_CHILDREN: + workStack.push(() => (el.textContent = '')); + break; + case VFlags.ONLY_TEXT_CHILDREN: + // Joining is faster than setting textContent to an array + workStack.push( + () => (el.textContent = (newVNode).children!.join('')), + ); + break; + default: + customPatchChildren( + el, + (oldVNode)?.children || [], + (newVNode).children!, + (newVNode).flag === VFlags.ONLY_KEYED_CHILDREN, + // We need to pass delta here because this function does not have + // a reference to the actual vnode. + (newVNode).delta, + workStack, + ); + break; + } } } } } - } - finish(); - return el; -}; + finish(); + return el; + }; + +/** + * Diffs two VNodes and modifies the DOM node based on the necessary changes + */ +export const patch = init();