The WebComponents.Dev site blogs about 51 ways/languages to make a <my-counter> Web Component
Native code ranks first in the File Size Ranking
But... I am totally against comparison with Svelte, because the Svelte compiler optimizes code
So if I "play compiler"
That native HTMLElement code can/may be optimized 
(and so can all other source versions)
| gzip | |
|---|---|
| original by WebComponents DEV | Β 476 B* | 
| Svelte | 1884 B | 
| refactored | Β 355 B - savings: 25% | 
| optimized | Β 339 B - savings: 29% | 
*) The Web Components DEV site says the file is 505 Bytes, 
copied to GitHub.io my <file-size> Web Component reports it is 476 Bytes.
Original <my-counter> = 476 Bytes:
Refactor code
See JavaScript Tab in above JSFiddle.
- Template literals are great, but they suck up bytes, as meaningless white space and - \nnewlines are still included in the minified file.
- no need for a - createElement('template')when we only want the innerHTML once
- template/content should not be added in the connectedCallback (as it can run multiple times) 
- super()sets and returns the this scope
- attachShadow()sets and returns- this.shadowRoot
- 
so everything can be chained: 
 constructor() { super() .attachShadow({ mode: 'open' }) .innerHTML = "<style>*{font-size:200%}...
- 
no need for a MyCounter class definition when it is used only once 
 customElements.define('my-counter', class extends HTMLElement {}
- 
Nearly all of the 51 examples use inline event handlers (notation). 
 render() { return html` <button @click="${this.dec}">-</button> <span>${this.count}</span> <button @click="${this.inc}">+</button> `; }
- 
Then we can do that as well - we have to add extra code to find the inc()anddec()methods on the element (which libraries do for you under the hood)
- the idreferences on the buttons are no longer needed
 <button onclick="this.getRootNode().host.inc()"> <button onclick="this.getRootNode().host.dec()">
- we have to add extra code to find the 
- The Component uses shadowRoot to encapsulate styles and content. The - idon- <span id="count">is not required because we can target the only- <span>that exists in shadowDOM
- 
All the refactored connectedCallbackdoes is set the span innerHTML to 0
 connectedCallback() { this.update(this.count); }Set the default 0 in HTML, and the connectedCallback is no longer required 
 "<span>0</span>"+
- remove not required white space and ; from CSS 
- 
remove not required quotes from HTML attributes, because the Browser will add them 
 <button onclick=this.getRootNode().host.inc()> <button onclick=this.getRootNode().host.dec()>
Refactored code = 355 Bytes:
customElements.define("my-counter", class extends HTMLElement {
    constructor() {
      super()
        .attachShadow({ mode: "open" })
        .innerHTML =
        "<style>" +
        "*{font-size:200%}"+
        "span{width:4rem;display:inline-block;text-align:center}" +
        "button{width:4rem;height:4rem;border:none;border-radius:10px;background-color:seagreen;color:white}" +
        "</style>" +
        "<button onclick=this.getRootNode().host.dec()>-</button>" +
        "<span>0</span>" +
        "<button onclick=this.getRootNode().host.inc()>+</button>";
      this.count = 0;
    }
    inc() {
      this.update(++this.count);
    }
    dec() {
      this.update(--this.count);
    }
    update(count) {
      this.shadowRoot.querySelector("span").innerHTML = count;
    }
  }
);
Optimized code = 339 Bytes:
This Component can be made better and even smaller
- The - inc,- decand- updatemethods are not required, when- countis made a getter/setter
- DRY (Don't Repeated Yourself) is great from a code maintenance Point-of-View. But from a delivery and performance PoV you do not want to be DRY; GZip loves repetitions 
 - * {font-size:200%}is applied to 2 elements only (button and span)
 Setting- font-size:200%on both elements creates a larger file, but a smaller GZipped file!
 (And the CSS parser has less work to do)
- .count-- >needs that extra space, to close the- onclickdefinition, or the minifier will add an- -escape code, adding 4 bytes.
- <span>can be replaced with- <p>
- no - this.count = 0;required because- <p>0</p>is the state
- replacing - seagreenand- whitewith shorter- #xxxnotation doesn't save extra bytes in this case, because the # doesn't exist yet in the code, thus requires extra GZip encoding bits.
 
customElements.define(
  "my-counter",
  class extends HTMLElement {
    constructor() {
      super().attachShadow({
        mode: "open",
      }).innerHTML =
        "<style>" +
        "p{font-size:200%;width:4rem;display:inline-block;text-align:center}" +
        "button{font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white}" +
        "</style>" +
        "<button onclick=this.getRootNode().host.count-- >-</button>" +
        "<p>0</p>" +
        "<button onclick=this.getRootNode().host.count++>+</button>";
    }
    set count(p) {
      this.shadowRoot.querySelector("p").innerHTML = p;
    }
    get count() {
      return ~~this.shadowRoot.querySelector("p").innerHTML;
    }
  }
);
Note: You can save 6 more bytes using INline styles 
but code maintainability suffers:
.innerHTML = 
"<button style=font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white onclick=this.getRootNode().host.count-- >-</button>" +
"<p style=font-size:200%;width:4rem;display:inline-block;text-align:center>0</p>" +
"<button style=font-size:200%;width:4rem;height:4rem;border:none;border-radius:10px;background:seagreen;color:white onclick=this.getRootNode().host.count++>+</button>"
smallesteding-ed Conclusion
Refactoring Web Components makes them smaller and better.
| gzip | |
|---|---|
| original by WebComponents.DEV | Β 476 B* | 
| Svelte | 1884 B | 
| refactored | Β 355 B - savings: 25% | 
| optimized | Β 339 B - savings: 29% | 
 
 
              

 
    
Top comments (0)