Skip to content

Commit

Permalink
[lit-html] Optimize setting primitive on ChildNode (#2076)
Browse files Browse the repository at this point in the history
Check if the previous value was a primitive rather than
checking the DOM for the presence of a single Text node.
This eliminates two DOM binding accesses.
  • Loading branch information
justinfagnani committed Aug 27, 2021
1 parent 0135331 commit 0d703bf
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-dragons-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'lit-html': patch
---

Optimize setting primitives on ChildNode.
7 changes: 6 additions & 1 deletion packages/labs/ssr/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./tsconfig.tsbuildinfo",
"target": "es2020",
"module": "esnext",
"lib": ["es2019", "ES2020.String"],
Expand All @@ -22,5 +23,9 @@
},
"include": ["src/**/*.ts", "custom_typings/*.d.ts"],
"exclude": [],
"references": [{"path": "../../lit-html"}, {"path": "../../lit-element"}]
"references": [
{"path": "../ssr-client"},
{"path": "../../lit-html"},
{"path": "../../lit-element"}
]
}
12 changes: 11 additions & 1 deletion packages/lit-html/src/directives/repeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,21 @@ class RepeatDirective extends Directive {
template
);

if (!oldParts) {
// We check that oldParts, the committed value, is an Array as an
// indicator that the previous value came from a repeat() call. If
// oldParts is not an Array then this is the first render and we return
// an array for lit-html's array handling to render, and remember the
// keys.
if (!Array.isArray(oldParts)) {
this._itemKeys = newKeys;
return newValues;
}

// In SSR hydration it's possible for oldParts to be an arrray but for us
// to not have item keys because the update() hasn't run yet. We set the
// keys to an empty array. This will cause all oldKey/newKey comparisons
// to fail and execution to fall to the last nested brach below which
// reuses the oldPart.
const oldKeys = (this._itemKeys ??= []);

// New part list will be built up as we go (either reused from
Expand Down
17 changes: 7 additions & 10 deletions packages/lit-html/src/lit-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ export type {ChildPart};
class ChildPart implements Disconnectable {
readonly type = CHILD_PART;
readonly options: RenderOptions | undefined;
_$committedValue: unknown;
_$committedValue: unknown = nothing;
/** @internal */
__directive?: Directive;
/** @internal */
Expand Down Expand Up @@ -1139,23 +1139,20 @@ class ChildPart implements Disconnectable {
}

private _commitText(value: unknown): void {
const node = wrap(this._$startNode).nextSibling;
// TODO(justinfagnani): Can we just check if this._$committedValue is primitive?
// If the committed value is a primitive it means we called _commitText on
// the previous render, and we know that this._$startNode.nextSibling is a
// Text node. We can now just replace the text content (.data) of the node.
if (
node !== null &&
node.nodeType === 3 /* Node.TEXT_NODE */ &&
(this._$endNode === null
? wrap(node).nextSibling === null
: node === wrap(this._$endNode).previousSibling)
this._$committedValue !== nothing &&
isPrimitive(this._$committedValue)
) {
const node = wrap(this._$startNode).nextSibling as Text;
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._textSanitizer === undefined) {
this._textSanitizer = createSanitizer(node, 'data', 'property');
}
value = this._textSanitizer(value);
}
// If we only have a single text node between the markers, we can just
// set its value, rather than replacing it.
(node as Text).data = value as string;
} else {
if (ENABLE_EXTRA_SECURITY_HOOKS) {
Expand Down

0 comments on commit 0d703bf

Please sign in to comment.