Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(engine-dom): bring back BaseUpgradableConstructor #4116

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ const nativeLifecycleElementsToUpgradedByLWC = new WeakMap<HTMLElement, boolean>

let elementBeingUpgradedByLWC = false;

// Creates a constructor that is intended to be used directly as a custom element, except that the upgradeCallback is
// passed in to the constructor so LWC can reuse the same custom element constructor for multiple components.
// Another benefit is that only LWC can create components that actually do anything – if you do
// `customElements.define('x-foo')`, then you don't have access to the upgradeCallback, so it's a dummy custom element.
// This class should be created once per tag name.
const createUpgradableConstructor = (isFormAssociated: boolean) => {
let BaseUpgradableConstructor: CustomElementConstructor | undefined;
let BaseHTMLElement: typeof HTMLElement | undefined;

function createBaseUpgradableConstructor() {
// Creates a constructor that is intended to be used directly as a custom element, except that the upgradeCallback is
// passed in to the constructor so LWC can reuse the same custom element constructor for multiple components.
// Another benefit is that only LWC can create components that actually do anything – if you do
// `customElements.define('x-foo')`, then you don't have access to the upgradeCallback, so it's a dummy custom element.
// This class should be created once per tag name.
// TODO [#2972]: this class should expose observedAttributes as necessary
class UpgradableConstructor extends HTMLElement {
BaseUpgradableConstructor = class TheBaseUpgradableConstructor extends HTMLElement {
constructor(upgradeCallback: LifecycleCallback, useNativeLifecycle: boolean) {
super();

Expand Down Expand Up @@ -79,12 +82,24 @@ const createUpgradableConstructor = (isFormAssociated: boolean) => {
runFormStateRestoreCallback(this, state, reason);
}
}
}
};
BaseHTMLElement = HTMLElement; // cache to track if it changes
}

const createUpgradableConstructor = (isFormAssociated: boolean) => {
if (HTMLElement !== BaseHTMLElement) {
// If the global HTMLElement changes out from under our feet, then we need to create a new
// BaseUpgradableConstructor from scratch (since it extends from HTMLElement). This can occur if
// polyfills are in play, e.g. a polyfill for scoped custom element registries.
// This workaround can potentially be removed when W-15361244 is resolved.
createBaseUpgradableConstructor();
}
// Using a BaseUpgradableConstructor superclass here is a perf optimization to avoid
// re-defining the same logic (connectedCallback, disconnectedCallback, etc.) over and over.
class UpgradableConstructor extends (BaseUpgradableConstructor!) {}
if (isFormAssociated) {
// Perf optimization - the vast majority of components have formAssociated=false,
// so we can skip the setter in those cases, since undefined works the same as false.
// @ts-expect-error type-mismatch
UpgradableConstructor.formAssociated = isFormAssociated;
}
return UpgradableConstructor;
Expand Down