-
Notifications
You must be signed in to change notification settings - Fork 426
perf(template-compiler): optimize all static nodes #2969
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||
| /* | ||||||
| * Copyright (c) 2018, salesforce.com, 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 { isUndefined } from '@lwc/shared'; | ||||||
|
|
||||||
| const SUPPORTS_TEMPLATE = typeof HTMLTemplateElement === 'function'; | ||||||
|
|
||||||
| export let createFragment: (html: string) => Node | null; | ||||||
|
|
||||||
| if (SUPPORTS_TEMPLATE) { | ||||||
| // Parse the fragment HTML string into DOM | ||||||
| createFragment = function (html: string) { | ||||||
| const template = document.createElement('template'); | ||||||
| template.innerHTML = html; | ||||||
| return template.content.firstChild; | ||||||
| }; | ||||||
| } else { | ||||||
| // In browsers that don't support <template> (e.g. IE11), we need to be careful to wrap elements like | ||||||
| // <td> in the proper container elements (e.g. <tbody>), because otherwise they will be parsed as null. | ||||||
|
|
||||||
| // Via https://github.com/webcomponents/polyfills/blob/ee1db33/packages/template/template.js#L273-L280 | ||||||
| // With other elements added from: | ||||||
| // https://github.com/sindresorhus/html-tags/blob/95dcdd5/index.js | ||||||
| // Using the test: | ||||||
| // document.createRange().createContextualFragment(`<${tag}></${tag}>`).firstChild === null | ||||||
| // And omitting <html>, <head>, and <body> as these are not practical in an LWC component. | ||||||
| const topLevelWrappingMap: { [key: string]: string[] } = { | ||||||
| caption: ['table'], | ||||||
| col: ['colgroup', 'table'], | ||||||
| colgroup: ['table'], | ||||||
| option: ['select'], | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch! Fixed and added a test to make sure it actually works in IE11. I did not include |
||||||
| tbody: ['table'], | ||||||
| td: ['tr', 'tbody', 'table'], | ||||||
| th: ['tr', 'tbody', 'table'], | ||||||
| thead: ['table'], | ||||||
| tfoot: ['table'], | ||||||
| tr: ['tbody', 'table'], | ||||||
| }; | ||||||
|
|
||||||
| // Via https://github.com/webcomponents/polyfills/blob/ee1db33/packages/template/template.js#L282-L288 | ||||||
| const getTagName = function (text: string) { | ||||||
| return (/<([a-z][^/\0>\x20\t\r\n\f]+)/i.exec(text) || ['', ''])[1].toLowerCase(); | ||||||
| }; | ||||||
|
|
||||||
| // Via https://github.com/webcomponents/polyfills/blob/ee1db33/packages/template/template.js#L295-L320 | ||||||
| createFragment = function (html: string) { | ||||||
| const wrapperTags = topLevelWrappingMap[getTagName(html)]; | ||||||
| if (!isUndefined(wrapperTags)) { | ||||||
| for (const wrapperTag of wrapperTags) { | ||||||
| html = `<${wrapperTag}>${html}</${wrapperTag}>`; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // For IE11, the document title must not be undefined, but it can be an empty string | ||||||
| // https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createHTMLDocument#browser_compatibility | ||||||
| const doc = document.implementation.createHTMLDocument(''); | ||||||
| doc.body.innerHTML = html; | ||||||
|
|
||||||
| let content: Node = doc.body; | ||||||
| if (!isUndefined(wrapperTags)) { | ||||||
| for (let i = 0; i < wrapperTags.length; i++) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Last I checked, JS engines have optimized this; there's no need to cache the There is some discussion on SO, although sadly all the JSPerf links are dead now. 😢
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, found a post from a V8 engineer; we do not need to cache |
||||||
| content = content.firstChild!; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return content.firstChild; | ||||||
| }; | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <template> | ||
| <!-- Separating these with individual "if" blocks causes the compiler to make each one a static fragment --> | ||
| <template if:true={doRender}> | ||
| <caption></caption> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <col> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <colgroup></colgroup> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <tbody></tbody> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <td></td> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <tfoot></tfoot> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <th></th> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <thead></thead> | ||
| </template> | ||
| <template if:true={doRender}> | ||
| <tr></tr> | ||
| </template> | ||
| </template> | ||
| </template> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { LightningElement, api } from 'lwc'; | ||
|
|
||
| export default class extends LightningElement { | ||
| @api doRender = false; | ||
| } |
This file was deleted.
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 considered caching and re-using the
templatehere, but I decided against it because:templatestrikes me as a little weirdWe might explore caching the
templatein a follow-up PR, though.