Skip to content

Commit

Permalink
refactor(engine-core): Simplify props and attrs patching (#2630)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmdartus authored Jan 14, 2022
1 parent a2a9da2 commit 17dc495
Show file tree
Hide file tree
Showing 15 changed files with 99 additions and 207 deletions.
2 changes: 1 addition & 1 deletion packages/@lwc/engine-core/src/3rdparty/snabbdom/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export interface VNodeData {
classMap?: Record<string, boolean>;
styleDecls?: Array<[string, string, boolean]>;
context?: Record<string, Record<string, any>>;
on?: Record<string, Function>;
on?: Record<string, (event: Event) => any>;
svg?: boolean;
}

Expand Down
13 changes: 5 additions & 8 deletions packages/@lwc/engine-core/src/framework/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,8 @@ import {
updateNodeHook,
insertNodeHook,
removeNodeHook,
createElmHook,
updateElmHook,
createCustomElmHook,
updateCustomElmHook,
patchChildren,
patchElementPropsAndAttrs,
allocateChildrenHook,
markAsDynamicChildren,
hydrateChildrenHook,
Expand Down Expand Up @@ -168,10 +165,10 @@ const ElementHook: Hooks<VElement> = {
fallbackElmHook(elm, vnode);
vnode.elm = elm;

createElmHook(vnode);
patchElementPropsAndAttrs(null, vnode);
},
update: (oldVnode, vnode) => {
updateElmHook(oldVnode, vnode);
patchElementPropsAndAttrs(oldVnode, vnode);
patchChildren(vnode.elm!, oldVnode.children, vnode.children);
},
insert: (vnode, parentNode, referenceNode) => {
Expand Down Expand Up @@ -244,10 +241,10 @@ const CustomElementHook: Hooks<VCustomElement> = {
} else if (vnode.ctor !== UpgradableConstructor) {
throw new TypeError(`Incorrect Component Constructor`);
}
createCustomElmHook(vnode);
patchElementPropsAndAttrs(null, vnode);
},
update: (oldVnode, vnode) => {
updateCustomElmHook(oldVnode, vnode);
patchElementPropsAndAttrs(oldVnode, vnode);
const vm = getAssociatedVMIfPresent(vnode.elm);
if (vm) {
// in fallback mode, the allocation will always set children to
Expand Down
83 changes: 23 additions & 60 deletions packages/@lwc/engine-core/src/framework/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ import { getClassList, setText, getAttribute, remove, insert } from '../renderer
import { EmptyArray, parseStyleText } from './utils';
import { createVM, getAssociatedVMIfPresent, VM, ShadowMode, RenderMode } from './vm';
import { VNode, VCustomElement, VElement, VNodes } from '../3rdparty/snabbdom/types';
import modEvents from './modules/events';
import modAttrs from './modules/attrs';
import modProps from './modules/props';
import modComputedClassName from './modules/computed-class-attr';
import modComputedStyle from './modules/computed-style-attr';
import modStaticClassName from './modules/static-class-attr';
import modStaticStyle from './modules/static-style-attr';
import { updateDynamicChildren, updateStaticChildren } from '../3rdparty/snabbdom/snabbdom';
import { patchElementWithRestrictions, unlockDomMutation, lockDomMutation } from './restrictions';
import { getComponentInternalDef } from './def';
import { markComponentAsDirty } from './component';
import { logError } from '../shared/logger';

import { patchAttributes } from './modules/attrs';
import { patchProps } from './modules/props';
import { patchClassAttribute } from './modules/computed-class-attr';
import { patchStyleAttribute } from './modules/computed-style-attr';
import { applyEventListeners } from './modules/events';
import { applyStaticClassAttribute } from './modules/static-class-attr';
import { applyStaticStyleAttribute } from './modules/static-style-attr';

function observeElementChildNodes(elm: Element) {
(elm as any).$domManual$ = true;
}
Expand Down Expand Up @@ -84,33 +85,28 @@ export function removeNodeHook(vnode: VNode, parentNode: Node) {
}
}

export function createElmHook(vnode: VElement) {
modEvents.create(vnode);
// Attrs need to be applied to element before props
// IE11 will wipe out value on radio inputs if value
// is set before type=radio.
modAttrs.create(vnode);
modProps.create(vnode);
modStaticClassName.create(vnode);
modStaticStyle.create(vnode);
modComputedClassName.create(vnode);
modComputedStyle.create(vnode);
export function patchElementPropsAndAttrs(oldVnode: VElement | null, vnode: VElement) {
if (isNull(oldVnode)) {
applyEventListeners(vnode);
applyStaticClassAttribute(vnode);
applyStaticStyleAttribute(vnode);
}

// Attrs need to be applied to element before props IE11 will wipe out value on radio inputs if
// value is set before type=radio.
patchClassAttribute(oldVnode, vnode);
patchStyleAttribute(oldVnode, vnode);
patchAttributes(oldVnode, vnode);
patchProps(oldVnode, vnode);
}

export const enum LWCDOMMode {
manual = 'manual',
}

export function hydrateElmHook(vnode: VElement) {
modEvents.create(vnode);
// Attrs are already on the element.
// modAttrs.create(vnode);
modProps.create(vnode);
// Already set.
// modStaticClassName.create(vnode);
// modStaticStyle.create(vnode);
// modComputedClassName.create(vnode);
// modComputedStyle.create(vnode);
applyEventListeners(vnode);
patchProps(null, vnode);
}

export function fallbackElmHook(elm: Element, vnode: VElement) {
Expand Down Expand Up @@ -146,16 +142,6 @@ export function fallbackElmHook(elm: Element, vnode: VElement) {
}
}

export function updateElmHook(oldVnode: VElement, vnode: VElement) {
// Attrs need to be applied to element before props
// IE11 will wipe out value on radio inputs if value
// is set before type=radio.
modAttrs.update(oldVnode, vnode);
modProps.update(oldVnode, vnode);
modComputedClassName.update(oldVnode, vnode);
modComputedStyle.update(oldVnode, vnode);
}

export function patchChildren(parent: ParentNode, oldCh: VNodes, newCh: VNodes) {
if (hasDynamicChildren(newCh)) {
updateDynamicChildren(parent, oldCh, newCh);
Expand Down Expand Up @@ -219,19 +205,6 @@ export function createViewModelHook(elm: HTMLElement, vnode: VCustomElement) {
}
}

export function createCustomElmHook(vnode: VCustomElement) {
modEvents.create(vnode);
// Attrs need to be applied to element before props
// IE11 will wipe out value on radio inputs if value
// is set before type=radio.
modAttrs.create(vnode);
modProps.create(vnode);
modStaticClassName.create(vnode);
modStaticStyle.create(vnode);
modComputedClassName.create(vnode);
modComputedStyle.create(vnode);
}

export function createChildrenHook(vnode: VElement) {
const { elm, children } = vnode;
for (let j = 0; j < children.length; ++j) {
Expand Down Expand Up @@ -417,16 +390,6 @@ export function hydrateChildrenHook(elmChildren: NodeListOf<ChildNode>, children
}
}

export function updateCustomElmHook(oldVnode: VCustomElement, vnode: VCustomElement) {
// Attrs need to be applied to element before props
// IE11 will wipe out value on radio inputs if value
// is set before type=radio.
modAttrs.update(oldVnode, vnode);
modProps.update(oldVnode, vnode);
modComputedClassName.update(oldVnode, vnode);
modComputedStyle.update(oldVnode, vnode);
}

export function removeElmHook(vnode: VElement) {
// this method only needs to search on child vnodes from template
// to trigger the remove hook just in case some of those children
Expand Down
43 changes: 14 additions & 29 deletions packages/@lwc/engine-core/src/framework/modules/attrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,42 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { assert, isNull, isUndefined, keys, StringCharCodeAt } from '@lwc/shared';

import { setAttribute, removeAttribute } from '../../renderer';
import { VElement } from '../../3rdparty/snabbdom/types';

import { unlockAttribute, lockAttribute } from '../attributes';
import { EmptyObject } from '../utils';
import { VElement } from '../../3rdparty/snabbdom/types';

const xlinkNS = 'http://www.w3.org/1999/xlink';
const xmlNS = 'http://www.w3.org/XML/1998/namespace';
const ColonCharCode = 58;

function updateAttrs(oldVnode: VElement, vnode: VElement) {
const {
data: { attrs },
} = vnode;

export function patchAttributes(oldVnode: VElement | null, vnode: VElement) {
const { attrs } = vnode.data;
if (isUndefined(attrs)) {
return;
}
let {
data: { attrs: oldAttrs },
} = oldVnode;

const oldAttrs = isNull(oldVnode) ? EmptyObject : oldVnode.data.attrs;
if (oldAttrs === attrs) {
return;
}

if (process.env.NODE_ENV !== 'production') {
assert.invariant(
isUndefined(oldAttrs) || keys(oldAttrs).join(',') === keys(attrs).join(','),
oldAttrs === EmptyObject || keys(oldAttrs).join(',') === keys(attrs).join(','),
`vnode.data.attrs cannot change shape.`
);
}

const elm = vnode.elm!;

let key: string;
oldAttrs = isUndefined(oldAttrs) ? EmptyObject : oldAttrs;

// update modified attributes, add new attributes
// this routine is only useful for data-* attributes in all kind of elements
// and aria-* in standard elements (custom elements will use props for these)
for (key in attrs) {
const { elm } = vnode;
for (const key in attrs) {
const cur = attrs[key];
const old = (oldAttrs as any)[key];
const old = oldAttrs[key];

if (old !== cur) {
unlockAttribute(elm, key);
unlockAttribute(elm!, key);
if (StringCharCodeAt.call(key, 3) === ColonCharCode) {
// Assume xml namespace
setAttribute(elm, key, cur as string, xmlNS);
Expand All @@ -60,14 +52,7 @@ function updateAttrs(oldVnode: VElement, vnode: VElement) {
} else {
setAttribute(elm, key, cur as string);
}
lockAttribute(elm, key);
lockAttribute(elm!, key);
}
}
}

const emptyVNode = { data: {} } as VElement;

export default {
create: (vnode: VElement) => updateAttrs(emptyVNode, vnode),
update: updateAttrs,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { create, freeze, isString, isUndefined, StringCharCodeAt, StringSlice } from '@lwc/shared';
import {
create,
freeze,
isNull,
isString,
isUndefined,
StringCharCodeAt,
StringSlice,
} from '@lwc/shared';

import { getClassList } from '../../renderer';
import { EmptyObject, SPACE_CHAR } from '../utils';
import { VElement } from '../../3rdparty/snabbdom/types';

import { EmptyObject, SPACE_CHAR } from '../utils';

const classNameToClassMap = create(null);

function getMapFromClassName(className: string | undefined): Record<string, boolean> {
Expand Down Expand Up @@ -47,14 +57,13 @@ function getMapFromClassName(className: string | undefined): Record<string, bool
return map;
}

function updateClassAttribute(oldVnode: VElement, vnode: VElement) {
export function patchClassAttribute(oldVnode: VElement | null, vnode: VElement) {
const {
elm,
data: { className: newClass },
} = vnode;
const {
data: { className: oldClass },
} = oldVnode;

const oldClass = isNull(oldVnode) ? undefined : oldVnode.data.className;
if (oldClass === newClass) {
return;
}
Expand All @@ -76,10 +85,3 @@ function updateClassAttribute(oldVnode: VElement, vnode: VElement) {
}
}
}

const emptyVNode = { data: {} } as VElement;

export default {
create: (vnode: VElement) => updateClassAttribute(emptyVNode, vnode),
update: updateClassAttribute,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import { isString } from '@lwc/shared';
import { isNull, isString } from '@lwc/shared';

import { setAttribute, removeAttribute } from '../../renderer';
import { VNode } from '../../3rdparty/snabbdom/types';
import { VElement } from '../../3rdparty/snabbdom/types';

// The style property is a string when defined via an expression in the template.
function updateStyleAttribute(oldVnode: VNode, vnode: VNode) {
export function patchStyleAttribute(oldVnode: VElement | null, vnode: VElement) {
const {
elm,
data: { style: newStyle },
} = vnode;
if (oldVnode.data.style === newStyle) {

const oldStyle = isNull(oldVnode) ? undefined : oldVnode.data.style;
if (oldStyle === newStyle) {
return;
}

Expand All @@ -24,10 +27,3 @@ function updateStyleAttribute(oldVnode: VNode, vnode: VNode) {
setAttribute(elm, 'style', newStyle);
}
}

const emptyVNode = { data: {} } as VNode;

export default {
create: (vnode: VNode) => updateStyleAttribute(emptyVNode, vnode),
update: updateStyleAttribute,
};
Loading

0 comments on commit 17dc495

Please sign in to comment.