From 9d5fa85d74202be287456dfcdf8cd9a5f069b345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sampo=20Kivist=C3=B6?= Date: Wed, 12 Jan 2022 08:55:14 +0200 Subject: [PATCH] WIP: Static vnode optimization --- jest.ts.transform.js | 2 +- packages/inferno-vnode-flags/src/index.ts | 1 + packages/inferno/src/DOM/mounting.ts | 18 ++++++++++++++++-- packages/inferno/src/DOM/patching.ts | 4 ++++ packages/inferno/src/DOM/utils/common.ts | 4 ++++ packages/inferno/src/core/implementation.ts | 12 ++++++++---- packages/inferno/src/core/types.ts | 1 + 7 files changed, 35 insertions(+), 7 deletions(-) diff --git a/jest.ts.transform.js b/jest.ts.transform.js index d3e921679..390f16b15 100644 --- a/jest.ts.transform.js +++ b/jest.ts.transform.js @@ -15,7 +15,7 @@ const transformer = babelJest.createTransformer({ ] ], plugins: [ - ["babel-plugin-inferno", {"imports": true}], + ["babel-plugin-inferno", {"imports": true, defineAllArguments: false, optimizeStaticVNodes: true}], ["@babel/plugin-proposal-class-properties", { "loose": true }] ] }); diff --git a/packages/inferno-vnode-flags/src/index.ts b/packages/inferno-vnode-flags/src/index.ts index aa783d817..a18b3dc03 100644 --- a/packages/inferno-vnode-flags/src/index.ts +++ b/packages/inferno-vnode-flags/src/index.ts @@ -12,6 +12,7 @@ export const enum VNodeFlags { InputElement = 1 << 6, TextareaElement = 1 << 7, SelectElement = 1 << 8, + IsStatic = 1 << 9, Portal = 1 << 10, ReCreate = 1 << 11, ContentEditable = 1 << 12, diff --git a/packages/inferno/src/DOM/mounting.ts b/packages/inferno/src/DOM/mounting.ts index 5d9b68365..df900ebbb 100644 --- a/packages/inferno/src/DOM/mounting.ts +++ b/packages/inferno/src/DOM/mounting.ts @@ -2,7 +2,16 @@ import type { VNode } from '../core/types'; import { isFunction, isNull, isNullOrUndef, isString, isStringOrNumber, throwError } from 'inferno-shared'; import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags'; import { createVoidVNode, directClone, normalizeRoot } from '../core/implementation'; -import { AnimationQueues, documentCreateElement, EMPTY_OBJ, findDOMfromVNode, insertOrAppend, safeCall1, setTextContent } from './utils/common'; +import { + AnimationQueues, + documentCloneDom, + documentCreateElement, + EMPTY_OBJ, + findDOMfromVNode, + insertOrAppend, + safeCall1, + setTextContent +} from './utils/common'; import { mountProps } from './props'; import { createClassComponentInstance, renderFunctionalComponent } from './utils/componentUtil'; import { validateKeys } from '../core/validate'; @@ -19,7 +28,9 @@ export function mount( ): void { const flags = (vNode.flags |= VNodeFlags.InUse); - if (flags & VNodeFlags.Element) { + if ((flags & VNodeFlags.IsStatic) > 0 && vNode.template && vNode.template.dom) { + vNode.dom = documentCloneDom(vNode.template.dom) + } if (flags & VNodeFlags.Element) { mountElement(vNode, parentDOM, context, isSVG, nextNode, lifecycle, animations); } else if (flags & VNodeFlags.ComponentClass) { mountClassComponent(vNode, parentDOM, context, isSVG, nextNode, lifecycle, animations); @@ -43,6 +54,9 @@ export function mount( throwError(`mount() expects a valid VNode, instead it received an object with the type "${typeof vNode}".`); } } + if ((flags & VNodeFlags.IsStatic) > 0 && !vNode.template) { + vNode.template = vNode; + } } function mountPortal(vNode, context, parentDOM: Element | null, nextNode: Element | null, lifecycle: Function[], animations: AnimationQueues) { diff --git a/packages/inferno/src/DOM/patching.ts b/packages/inferno/src/DOM/patching.ts index d1ccb989b..5934c3874 100644 --- a/packages/inferno/src/DOM/patching.ts +++ b/packages/inferno/src/DOM/patching.ts @@ -49,6 +49,10 @@ export function patch( ) { const nextFlags = (nextVNode.flags |= VNodeFlags.InUse); + if ((nextFlags & VNodeFlags.IsStatic) > 0 && nextVNode.template === lastVNode.template) { + return; + } + if (process.env.NODE_ENV !== 'production') { if (isFunction(options.componentComparator) && lastVNode.flags & nextFlags & VNodeFlags.ComponentClass) { if ((options.componentComparator as Function)(lastVNode, nextVNode) === false) { diff --git a/packages/inferno/src/DOM/utils/common.ts b/packages/inferno/src/DOM/utils/common.ts index c2297f58f..65fdf439c 100644 --- a/packages/inferno/src/DOM/utils/common.ts +++ b/packages/inferno/src/DOM/utils/common.ts @@ -47,6 +47,10 @@ export function insertOrAppend(parentDOM: Element, newNode, nextNode) { } } +export function documentCloneDom(dom: Element): Element { + return dom.cloneNode(true) as Element; +} + export function documentCreateElement(tag, isSVG: boolean): Element { if (isSVG) { return document.createElementNS('http://www.w3.org/2000/svg', tag); diff --git a/packages/inferno/src/core/implementation.ts b/packages/inferno/src/core/implementation.ts index 9c392bd65..c92ff3fc1 100644 --- a/packages/inferno/src/core/implementation.ts +++ b/packages/inferno/src/core/implementation.ts @@ -6,7 +6,7 @@ import { Fragment, mergeUnsetProperties, options } from './../DOM/utils/common'; const keyPrefix = '$'; -function V(childFlags: ChildFlags, children, className: string | null | undefined, flags: VNodeFlags, key, props, ref, type) { +function V(childFlags: ChildFlags, children, className: string | null | undefined, flags: VNodeFlags, key, props, ref, type, template: VNode | null) { if (process.env.NODE_ENV !== 'production') { this.isValidated = false; } @@ -19,6 +19,7 @@ function V(childFlags: ChildFlags, children, className: string | null | undefine this.props = props === void 0 ? null : props; this.ref = ref === void 0 ? null : ref; this.type = type; + this.template = template; } export function createVNode

( @@ -37,7 +38,7 @@ export function createVNode

( } } const childFlag: ChildFlags = childFlags === void 0 ? ChildFlags.HasInvalidChildren : (childFlags as ChildFlags); - const vNode = new V(childFlag, children, className, flags, key, props, ref, type) as VNode; + const vNode = new V(childFlag, children, className, flags, key, props, ref, type, null) as VNode; if (options.createVNode) { options.createVNode(vNode); @@ -126,7 +127,8 @@ export function createComponentVNode

( key, mergeDefaultProps(flags, type, props), mergeDefaultHooks(flags, type, ref), - type + type, + null ) as VNode; if (options.createVNode) { @@ -145,6 +147,7 @@ export function createTextVNode(text?: string | boolean | null | number, key?: s key, null, null, + null, null ) as VNode; } @@ -241,7 +244,8 @@ export function directClone(vNodeToClone: VNode): VNode { vNodeToClone.key, props, vNodeToClone.ref, - vNodeToClone.type + vNodeToClone.type, + (flags & VNodeFlags.IsStatic) > 0 ? vNodeToClone : null ) as VNode; } diff --git a/packages/inferno/src/core/types.ts b/packages/inferno/src/core/types.ts index 6c8f46cd6..bd2004c3d 100644 --- a/packages/inferno/src/core/types.ts +++ b/packages/inferno/src/core/types.ts @@ -118,6 +118,7 @@ export interface VNode { key: null | number | string; props: any; ref: any; + template: VNode | null; type: any; }