-
Notifications
You must be signed in to change notification settings - Fork 396
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf(engine-core): reduce fragment cache objects (#4431)
- Loading branch information
1 parent
c7124c8
commit fd3c9e6
Showing
13 changed files
with
219 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright (c) 2024, Salesforce, Inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: MIT | ||
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT | ||
*/ | ||
import { ArrayFrom } from '@lwc/shared'; | ||
|
||
export const enum FragmentCacheKey { | ||
HAS_SCOPED_STYLE = 1, | ||
SHADOW_MODE_SYNTHETIC = 2, | ||
} | ||
|
||
// HAS_SCOPED_STYLE | SHADOW_MODE_SYNTHETIC = 3 | ||
const MAX_CACHE_KEY = 3; | ||
|
||
// Mapping of cacheKeys to `string[]` (assumed to come from a tagged template literal) to an Element. | ||
// Note that every unique tagged template literal will have a unique `string[]`. So by using `string[]` | ||
// as the WeakMap key, we effectively associate each Element with a unique tagged template literal. | ||
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates | ||
// Also note that this array only needs to be large enough to account for the maximum possible cache key | ||
const fragmentCache: WeakMap<string[], Element>[] = ArrayFrom( | ||
{ length: MAX_CACHE_KEY + 1 }, | ||
() => new WeakMap() | ||
); | ||
|
||
// Only used in LWC's Karma tests | ||
if (process.env.NODE_ENV === 'test-karma-lwc') { | ||
(window as any).__lwcResetFragmentCache = () => { | ||
for (let i = 0; i < fragmentCache.length; i++) { | ||
fragmentCache[i] = new WeakMap(); | ||
} | ||
}; | ||
} | ||
|
||
function checkIsBrowser() { | ||
// The fragment cache only serves prevent calling innerHTML multiple times which doesn't happen on the server. | ||
/* istanbul ignore next */ | ||
if (!process.env.IS_BROWSER) { | ||
throw new Error( | ||
'The fragment cache is intended to only be used in @lwc/engine-dom, not @lwc/engine-server' | ||
); | ||
} | ||
} | ||
|
||
export function getFromFragmentCache(cacheKey: number, strings: string[]) { | ||
checkIsBrowser(); | ||
return fragmentCache[cacheKey].get(strings); | ||
} | ||
|
||
export function setInFragmentCache(cacheKey: number, strings: string[], element: Element) { | ||
checkIsBrowser(); | ||
fragmentCache[cacheKey].set(strings, element); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
packages/@lwc/integration-karma/test/rendering/fragment-cache/index.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { createElement } from 'lwc'; | ||
import { LOWERCASE_SCOPE_TOKENS } from 'test-utils'; | ||
|
||
import NativeScopedStyles from 'x/nativeScopedStyles'; | ||
import NativeStyles from 'x/nativeStyles'; | ||
import NoStyles from 'x/noStyles'; | ||
import ScopedStyles from 'x/scopedStyles'; | ||
import Styles from 'x/styles'; | ||
|
||
const scenarios = [ | ||
{ | ||
name: 'no styles', | ||
Ctor: NoStyles, | ||
tagName: 'x-no-styles', | ||
expectedColor: 'rgb(0, 0, 0)', | ||
expectClass: false, | ||
expectAttribute: false, | ||
}, | ||
{ | ||
name: 'styles', | ||
Ctor: Styles, | ||
tagName: 'x-styles', | ||
expectedColor: 'rgb(255, 0, 0)', | ||
expectClass: false, | ||
expectAttribute: !process.env.NATIVE_SHADOW, | ||
}, | ||
{ | ||
name: 'scoped styles', | ||
Ctor: ScopedStyles, | ||
tagName: 'x-scoped-styles', | ||
expectedColor: 'rgb(0, 128, 0)', | ||
expectClass: true, | ||
expectAttribute: !process.env.NATIVE_SHADOW, | ||
}, | ||
{ | ||
name: 'native styles', | ||
Ctor: NativeStyles, | ||
tagName: 'x-native-styles', | ||
expectedColor: 'rgb(255, 0, 0)', | ||
expectClass: false, | ||
expectAttribute: false, | ||
}, | ||
{ | ||
name: 'native scoped styles', | ||
Ctor: NativeScopedStyles, | ||
tagName: 'x-native-scoped-styles', | ||
expectedColor: 'rgb(0, 128, 0)', | ||
expectClass: true, | ||
expectAttribute: false, | ||
}, | ||
]; | ||
|
||
// These tests confirm that the fragment cache (from `fragment-cache.ts`) is working correctly. Fragments should be | ||
// unique based on 1) synthetic vs native shadow, and 2) presence or absence of scoped styles. If the fragment cache is | ||
// not working correctly, then we may end up rendering the wrong styles or the wrong attribute/class scope token due to | ||
// the cache being poisoned, e.g. an HTML string for scoped styles being rendered for non-scoped styles. | ||
// To test this, we re-use the same `template.html` but change the `static stylesheets` in each component. | ||
scenarios.forEach(({ name, Ctor, tagName, expectedColor, expectClass, expectAttribute }) => { | ||
describe(name, () => { | ||
let h1; | ||
|
||
beforeEach(async () => { | ||
const elm = createElement(tagName, { is: Ctor }); | ||
document.body.appendChild(elm); | ||
await Promise.resolve(); | ||
h1 = elm.shadowRoot.querySelector('h1'); | ||
}); | ||
|
||
it('renders the correct styles', () => { | ||
expect(getComputedStyle(h1).color).toBe(expectedColor); | ||
}); | ||
|
||
it('renders the correct attributes/classes', () => { | ||
const scopeToken = LOWERCASE_SCOPE_TOKENS ? 'lwc-2it5vhebv0i' : 'x-template_template'; | ||
|
||
expect(h1.getAttribute('class')).toBe(expectClass ? scopeToken : null); | ||
expect(h1.hasAttribute(scopeToken)).toBe(expectAttribute); | ||
}); | ||
}); | ||
}); |
12 changes: 12 additions & 0 deletions
12
...ntegration-karma/test/rendering/fragment-cache/x/nativeScopedStyles/nativeScopedStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { LightningElement } from 'lwc'; | ||
import template from '../template/template.html'; | ||
import styles from '../stylesheets/scopedStyles.scoped.css'; | ||
|
||
export default class extends LightningElement { | ||
static shadowSupportMode = 'native'; | ||
static stylesheets = [styles]; | ||
|
||
render() { | ||
return template; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/nativeStyles/nativeStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { LightningElement } from 'lwc'; | ||
import template from '../template/template.html'; | ||
import styles from '../stylesheets/styles.css'; | ||
|
||
export default class extends LightningElement { | ||
static shadowSupportMode = 'native'; | ||
static stylesheets = [styles]; | ||
|
||
render() { | ||
return template; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/noStyles/noStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { LightningElement } from 'lwc'; | ||
import template from '../template/template.html'; | ||
|
||
export default class extends LightningElement { | ||
render() { | ||
return template; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/scopedStyles/scopedStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { LightningElement } from 'lwc'; | ||
import template from '../template/template.html'; | ||
import styles from '../stylesheets/scopedStyles.scoped.css'; | ||
|
||
export default class extends LightningElement { | ||
static stylesheets = [styles]; | ||
|
||
render() { | ||
return template; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/styles/styles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { LightningElement } from 'lwc'; | ||
import template from '../template/template.html'; | ||
import styles from '../stylesheets/styles.css'; | ||
|
||
export default class extends LightningElement { | ||
static stylesheets = [styles]; | ||
|
||
render() { | ||
return template; | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
...lwc/integration-karma/test/rendering/fragment-cache/x/stylesheets/scopedStyles.scoped.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
h1 { | ||
color: green; | ||
} |
3 changes: 3 additions & 0 deletions
3
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/stylesheets/styles.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
h1 { | ||
color: red; | ||
} |
3 changes: 3 additions & 0 deletions
3
packages/@lwc/integration-karma/test/rendering/fragment-cache/x/template/template.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<template> | ||
<h1>hello</h1> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters