-
Notifications
You must be signed in to change notification settings - Fork 393
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: special handling for static elements #2781
Conversation
09c68eb
to
4438a7d
Compare
4438a7d
to
1af0703
Compare
sel: undefined, | ||
key, | ||
elm: undefined, | ||
elmProto, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this property should probably be renamed to frag
or fragment
, using Proto
postfix is misleading.
@@ -49,6 +51,18 @@ function addVNodeToChildLWC(vnode: VCustomElement) { | |||
ArrayPush.call(getVMBeingRendered()!.velements, vnode); | |||
} | |||
|
|||
// [st]atic node | |||
function st(elmProto: Element, key: Key): VStatic { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not reusing VElementData here? and take the key out of the data? My hunch is that eventually we might be able to do some crazy optimizations where the data payload can provide some stuff for the subtree, so it makes sense to keep it around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
atm is not needed and this is internals that we could modify to add the data bag once we have an optimization in mind. passing the key directly will reduce size (fewer chars), and perf (destructing key from the databag). however, I can generate the databag if that is preferred.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good... simpler that I thought it was going to be. Minor details to figure out and some other silly questions :)
|
||
htmlFragments.push(strings[strings.length - 1]); | ||
|
||
return createFragment(htmlFragments.join('')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: I don't like the extra ""=""
, or the class=""
. what's your opinion about doing about stripping them away? at least in dev or test.
Notice that we can fix the errors in our repo, but I figure that there will be other repos with tests in the same situation.
Alternatively, what about doing our own serializer (sounds difficult)? to work around the fact that parse5 attributes value always needs to be defined (we can't generate `<div ${1}>).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope that we can avoid ""="", or the class="", let's chat
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved in 706a6fd (adds a custom serializer for static elements using our ast)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My intuition tells me that it would be faster at runtime to create elements and add attributes (i.e. document.createElement
, appendChild
, setAttribute
, etc.), rather than use createContextualFragment
. It would avoid parsing HTML on the client-side.
However, the tradeoff is that the current approach has a more compact code representation. And for very large static content (e.g. lots of nested elements), I think creating the fragment may actually be faster at runtime. So this seems like a good approach to me.
FWIW, I usually just use the "file filter" in the GitHub UI to filter out |
packages/integration-karma/test/api/sanitizeAttribute/x/xlink/xlink.html
Outdated
Show resolved
Hide resolved
This looks reasonable to me; it feels a bit simpler and more straightforward than the other PR. I'm curious to know what the perf improvement looks like? Nice work! |
81abb4d
to
941ce71
Compare
Confirmed that 95ce4c3 broke the test in this PR. |
Did an analysis of downstream Nucleus tests. Seems there are only 2 new failures, and they are due to minor snapshot differences. |
Blocked by #2859. |
Looks like this PR was severely bitrotted by da49079. Merge conflicts galore. In particular, this part seems problematic now: lwc/packages/@lwc/engine-core/src/framework/template.ts Lines 180 to 186 in 4d5b57e
We don't have access to the renderer anymore in |
// Engine-core public APIs ------------------------------------------------------------------------- | ||
export { parseFragment as parseSVGFragment }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we reuse the same parseFragment in server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the reason is because parseSVGFragment
uses getFirstChild
, which is not supported in the server environment:
lwc/packages/@lwc/engine-core/src/framework/template.ts
Lines 182 to 186 in 4d5b57e
export const parseSVGFragment = buildParseFragmentFn((html) => { | |
const fragment = createFragment('<svg>' + html + '</svg>'); | |
return getFirstChild(fragment); | |
}); |
If I try to use the actual parseSVGFragment
, I get an error in the Jest tests:
Click to see
Summary of all failing tests
FAIL packages/@lwc/engine-server/src/__tests__/fixtures.spec.ts (18.993 s)
● fixtures › svgs/index.js
TypeError: "getFirstChild" is not supported in this environment
13 | create,
14 | StringToLowerCase,
> 15 | htmlPropertyToAttribute,
| ^
16 | noop,
17 | } from '@lwc/shared';
18 |
at src/renderer.ts:15:15
at ../engine-core/dist/engine-core.cjs.js:4654:12
at ../engine-core/dist/engine-core.cjs.js:4645:31
at tmpl (src/__tests__/fixtures/svgs/dist/compiled.js:35:27)
at ../engine-core/dist/engine-core.cjs.js:4718:27
at ReactiveObserver.observe (../engine-core/dist/engine-core.cjs.js:339:13)
at isUpdatingTemplate (../engine-core/dist/engine-core.cjs.js:4674:13)
at runWithBoundaryProtection (../engine-core/dist/engine-core.cjs.js:5534:5)
at evaluateTemplate (../engine-core/dist/engine-core.cjs.js:4667:5)
at invokeComponentRenderMethod (../engine-core/dist/engine-core.cjs.js:4820:41)
wip: add a static vnode type wip: needs key and patch wip: all tests passing exept a few because of simple things wip: set style tokens for fragments during template evaluation wip: set element shadow token for fragments during template evaluation wip: propagate the shadow resolver key of static fragments wip: do not gen static nodes for text or comments wip: use tagedTemplate expression to replate stylesheetToken wip: use cloneNode from renderer wip: treeWalker to work in ie11 refactor: do not strip empty attr or empty class attr fix: using incorrect key wip: trim value of textNodes and review feedback fix: hydration feat: custom static element serializer wip: remove unessesary import fix: hydration fix: snapshot tests fix: missing karma test fix: test due rebase test: add test for static content needing nodeOwner fix: escape strings in serializer refactor: remove unused apis on generated code refactor: review suggestions fix: support mixed mode wip: fix compilation snapshots fix: increase 0.5kb bundlesize for engine dom fix: flapper wip: helpers.ts review wip: codegen.ts review wip: missing items from pm review wip: review comments fix: respect preserveComments and fuse $1,2 into 3 fix: svg content with the correct namespace feat(template-compiler): add option to disable static content optimizations wip: remove invalid comment chore: bump version to v2.13.0 (#2784) chore: dependencies upgrade (#2785) test: fix Node warning about event emitters (#2789) chore: run karma and integration tests in parallel (#2792) * chore: run karma and integration tests in parallel * fix: remove log lines fix(babel-plugin-component): remove import validation (#2719) test: remove flakey IE integration test (#2796) test: update test to use lwc imports (#2794) chore: Restrict further import order (#2795) chore: bump version to v2.13.1 (#2804) refactor(engine): moving vm references from dom into core (#2801) * refactor(engine): moving vm references from dom into core chore(nucleus): remove salesforcedevs/developer-website (#2807) test(integration-karma): small quality-of-life improvements (#2809) chore(deps): bump ejs from 3.1.6 to 3.1.7 (#2810) chore: weekly dependencies upgrade (#2816) * chore: weekly dependencies upgrade * fix: update yarn.lock` refactor(engine): optimize computation of transitive shadow mode (#2803) chore(deps): bump async from 2.6.3 to 2.6.4 (#2815) Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/caolan/async/releases) - [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md) - [Commits](caolan/async@v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: async dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> chore: bump version to v2.13.2 (#2819) chore: retry failed Circle CI tests (#2814) * chore: retry failed Circle CI tests W-10946477 * chore: fix * chore: fix * chore: fix * chore: fix * chore: fix * chore: fix * chore: fix * chore: deliberately fail a test to see what happens * chore: improve retry script * fix: whitespace * Revert "chore: deliberately fail a test to see what happens" This reverts commit 611fc34. * chore: rename to retry_karma fix(engine-core): add shim for old stylesheetTokens internal API (#2821) W-11093934 chore: bump version to v2.13.3 (#2823) fix(build): remove swc, switch back to babel and terser (#2818) feat: add freezeTemplate() API, warn on mutation (#2825) * feat: add freezeTemplate() API, warn on mutation * fix: warn on slots/renderMode as well * fix: add comment * fix: remove duplicate process.env.NODE_ENV check fix(engine-dom): refactor stylesheet API for consistency (#2827) * fix(engine-dom): refactor stylesheet API for consistency * fix: remove useless code comment * test: remove unnecessary test * test: remove unnecessary test * refactor: slight refactor * fix: add code comments * fix: add code comments * fix: add better comment fix: relax static id validation in iterations (#2830) fix(rollup-plugin): emit warnings during compilation (#2833) * fix(rollup-plugin): emit warnings during compilation Fixes #2771 W-10930894 * fix: add code comment fix(engine-dom): make feature flags work (#2812) * fix(engine-dom): make feature flags work Fixes #2811 * fix: license headers * test: fix jest tests * test: fix test * test: fix test * fix: use Eugene's technique instead * Revert "fix: use Eugene's technique instead" This reverts commit 72afdc0. * fix: use Eugene's technique instead * fix: revert unnecessary test change * fix: revert, use the elaborate test instead * fix: fix feature flags in engine-server as well perf(engine-dom): refactor style cache to reduce lookups (#2832) * perf(engine-dom): refactor style cache to reduce lookups * fix: tidy up comments * fix: update packages/@lwc/engine-dom/src/styles.ts Co-authored-by: Pierre-Marie Dartus <p.dartus@salesforce.com> * fix: remove semi * fix: remove "used" flag * fix: refactor * fix: refactor * fix: bring back "used" flag * fix: typo Co-authored-by: Pierre-Marie Dartus <p.dartus@salesforce.com> chore: update deps (#2838) test: run feature flag test code only in karma (#2835) fix: trigger slotchange event on removing slot (#2840) test(integration-karma): silence lwc rollup plugin warnings (#2836) * test(integration-karma): silence lwc rollup plugin warnings * fix: use warn API v2.11.7 (#2842) chore: release v2.14.0 (#2846) fix: only remove slot children in synthetic shadow (#2843) * fix: only remove slot children in synthetic shadow * fix: use case block fix: only add version mismatch test code in karma (#2852) test(integration-karma): ensure constructable stylesheets are re-used (#2844) * test(integration-karma): ensure constructable stylesheets are re-used * test: add test for shared style chore(nucleus): remove more downstreams (#2855) chore(nucleus): remove another downstream (#2857) docs: fix typo in template compiler readme (#2848) * docs: fix typo in template compiler readme * docs: rewording usage of lwc dynamic directive Co-authored-by: Eugene Kashida <ekashida@gmail.com> Co-authored-by: Eugene Kashida <ekashida@gmail.com> chore: fix lint test: refactor test, remove test covered in #2859 test: on second thought, bring test back
4d5b57e
to
ec12fce
Compare
@@ -30,6 +33,8 @@ export { | |||
setHooks, | |||
getComponentDef, | |||
isComponentConstructor, | |||
parseFragment, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be removed!
} | ||
|
||
// Note: at the moment this code executes, we don't have a renderer yet. | ||
export const parseFragment = buildParseFragmentFn((html, renderer) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unnecessary abstraction.
@@ -26,6 +26,8 @@ export { | |||
setHooks, | |||
getComponentDef, | |||
isComponentConstructor, | |||
parseFragment, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be removed...
@@ -26,6 +26,8 @@ export { | |||
setHooks, | |||
getComponentDef, | |||
isComponentConstructor, | |||
parseFragment, | |||
parseFragment as parseSVGFragment, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear why on the server side SVG can just reuse the other one... I don't remember this.
The biggest issue to be resolved here is the fact that, IMO, parsing HTML fragment SHOULD be considered risky all the time... meaning the template SHOULD import the renderer, and call the corresponding parsing method. Today, in this PR, it is not quite like that, what's going on now is the following:
The issue that I see is that the vm will always have what we call the default renderer, meaning locker will not be able to intersect that. This is fine if we make it very clear what can be considered static, but that's more tricky to track down, I prefer to just have an always inspectable parsing mechanism by importing |
Agreed, we should import the
That said, I still think some level of abstraction will be necessary. At least to abstract away the current |
|
That's correct. right now it is not sanitized, but the risk is not that big because LWC compiler parses and validates that HTML, but you never know. I'm fine to do it in another PR :) |
Awesome, thanks. Let's merge this, and I opened another issue to track |
Details
Perf
The regressions are from
1k-different-style
tests, and are because the component under test template is static, and it needs to replace the style token.Does this pull request introduce a breaking change?
Does this pull request introduce an observable change?
Because
class
andstyle
attributes of static nodes are not parsed into classMap and styleMap, it may be the case that they may contain extra characters in ssr and the dom. ex:<div class=" foo bar">
now generates exactly that, while before this pr it used to generate<div class="foo bar">