Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@patrick-steele-idem
Created April 6, 2017 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save patrick-steele-idem/7dbaf5ada60cb05d0d9482f183f0e7a4 to your computer and use it in GitHub Desktop.
Save patrick-steele-idem/7dbaf5ada60cb05d0d9482f183f0e7a4 to your computer and use it in GitHub Desktop.
Marko VDOM Optimizations

Marko VDOM Optimizations

This is a quick response to The Glimmer VM: Boots Fast and Stays Fast which references a modified js-framework-benchmark

While not at all identical to how Glimmer handles things, Marko also separates out static content from dynamic content to reduce the amount of work required to render the VDOM and diff/patch the real DOM (diffing is completely skipped for static subtrees and static attributes). For example, given the following Marko template that has only two dynamic ${label} parts mixed into the mostly static HTML content:

<li class="media">
  <div class="media-left">
    <a href="#">
      <img class="media-object" src="http://lorempixel.com/50/50/" alt="..." />
    </a>
  </div>
  <div class="media-body">
    <h4 class="media-heading">Media heading ${label}</h4>
    <button>Like</button>
    <button>Share</button>
    <button>Comment</button>
    <ul class="media-list">
      <li class="media">
        <div class="media-left">
          <a href="#">
            <img class="media-object" src="http://lorempixel.com/25/25" alt="..." />
          </a>
        </div>
        <div class="media-body">
          <h4>Chad Hietala</h4>
          Wat LulZ
        </div>
      </li>
      <li class="media">
        <div class="media-left">
          <a href="#">
            <img class="media-object" src="http://lorempixel.com/25/25" alt="..."/>
          </a>
        </div>
        <div class="media-body">
          <h4>Bill Hietala</h4>
          Wat
        </div>
      </li>
      <li class="media">
        <div class="media-left">
          <a href="#">
            <img class="media-object" src="http://lorempixel.com/25/25" alt="..." />
          </a>
        </div>
        <div class="media-body">
          <h4>Bob Hietala {label}</h4>
          Woot
        </div>
      </li>
    </ul>
  </div>
</li>

In the compiled code below you will notice that there is a render() function and outside the render function you will see VDOM nodes that are created once and used for every rerender. Inserting these static nodes into the rendered output have essentially zero cost at runtime since they are only created once when the template is first loaded. In addition, difffing/patching is skipped for static VDOM subtrees. On top of that, you will see that static attributes on non-static HTML elements are separated out into static variables and diffing of static attributes is skipped for also near-zero cost.

"use strict";

var marko_template = module.exports = require("marko/vdom").t(__filename),
    marko_attrs0 = {
        "class": "media"
      },
    marko_helpers = require("marko/runtime/vdom/helpers"),
    marko_createElement = marko_helpers.e,
    marko_const = marko_helpers.const,
    marko_const_nextId = marko_const("3fa209"),
    marko_node0 = marko_createElement("DIV", {
        "class": "media-left"
      }, 1, 0, marko_const_nextId())
      .e("A", {
          href: "#"
        }, 1)
        .e("IMG", {
            "class": "media-object",
            src: "http://lorempixel.com/50/50/",
            alt: "..."
          }, 0),
    marko_attrs1 = {
        "class": "media-body"
      },
    marko_attrs2 = {
        "class": "media-heading"
      },
    marko_node1 = marko_createElement("BUTTON", null, 1, 0, marko_const_nextId())
      .t("Like"),
    marko_node2 = marko_createElement("BUTTON", null, 1, 0, marko_const_nextId())
      .t("Share"),
    marko_node3 = marko_createElement("BUTTON", null, 1, 0, marko_const_nextId())
      .t("Comment"),
    marko_attrs3 = {
        "class": "media-list"
      },
    marko_node4 = marko_createElement("LI", {
        "class": "media"
      }, 2, 0, marko_const_nextId())
      .e("DIV", {
          "class": "media-left"
        }, 1)
        .e("A", {
            href: "#"
          }, 1)
          .e("IMG", {
              "class": "media-object",
              src: "http://lorempixel.com/25/25",
              alt: "..."
            }, 0)
      .e("DIV", {
          "class": "media-body"
        }, 2)
        .e("H4", null, 1)
          .t("Chad Hietala")
        .t(" Wat LulZ"),
    marko_node5 = marko_createElement("LI", {
        "class": "media"
      }, 2, 0, marko_const_nextId())
      .e("DIV", {
          "class": "media-left"
        }, 1)
        .e("A", {
            href: "#"
          }, 1)
          .e("IMG", {
              "class": "media-object",
              src: "http://lorempixel.com/25/25",
              alt: "..."
            }, 0)
      .e("DIV", {
          "class": "media-body"
        }, 2)
        .e("H4", null, 1)
          .t("Bill Hietala")
        .t(" Wat"),
    marko_attrs4 = {
        "class": "media"
      },
    marko_node6 = marko_createElement("DIV", {
        "class": "media-left"
      }, 1, 0, marko_const_nextId())
      .e("A", {
          href: "#"
        }, 1)
        .e("IMG", {
            "class": "media-object",
            src: "http://lorempixel.com/25/25",
            alt: "..."
          }, 0),
    marko_attrs5 = {
        "class": "media-body"
      };

function render(input, out) {
  var data = input;

  out.e("LI", marko_attrs0, 2)
    .n(marko_node0)
    .e("DIV", marko_attrs1, 5)
      .e("H4", marko_attrs2, 2)
        .t("Media heading ")
        .t(label)
      .n(marko_node1)
      .n(marko_node2)
      .n(marko_node3)
      .e("UL", marko_attrs3, 3)
        .n(marko_node4)
        .n(marko_node5)
        .e("LI", marko_attrs4, 2)
          .n(marko_node6)
          .e("DIV", marko_attrs5, 2)
            .e("H4", null, 2)
              .t("Bob Hietala ")
              .t(label)
            .t(" Woot");
}

marko_template._ = render;

While the js-framework-benchmark is interesting, we also found results to be very different with more real-world UI components: Marko benchmark results (benchmark source: https://github.com/marko-js/isomorphic-ui-benchmarks).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment