Building UI Like LEGO (With String Template Literals)
Some time ago I experimented and wrote about building composable UI using Polymer and <template>
was my
main building block. I used it to declare building blocks for my pages which I would dynamically interchange depending on
the displayed content. Unfortunately I’ve hit a number of roadblocks but I think I’ve just recently found a solution.
Just last week I attended the third Polymer Summit in Copenhagen where Justin Fagnani showed his
newest experiment: lit-html
. You should definitely watch his presentation:
What is the middle ground between full VDOM and manual DOM manipulation? Check out @justinfagnani’s lit-html https://t.co/a0aR7c70FV
— Surma (@DasSurma) August 29, 2017
The end of <dom-module>
Why is this important? Apparently, the next version of Polymer won’t directly use the <template>
tag. Instead, it will
be 100% JS. Yes, you heard correctly. No more <dom-module>
:
1 2 3 4 5 6 7 8 9 |
|
As you see, instead of HTML+JS, there is only code. Looks more like React+JSX, doesn’t it? It sparked heated discussions at the conference, on Polymer’s Slack channel and on Twitter.
At this stage though, the above template()
method returns a static HTML string, which is then injected into a HTML
template and later stamped into the element’s shadow root. Justin’s lit-html, akin to a number of earlier libraries takes
this one step further, thanks to the properties of JavaScript’s template literals (the backtick strings, duh!).
Template literals
Here’s an example how a basic component could use lit-html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
This is has all advantages of JS: scoping, syntax highlighting and suggestions and the ability to compose a template from
multiple other literals. This opens completely new possibilities where one can create decorator components or override
extensible points of parent element. Features which were very cumbersome with plain <template>
tags.
But most importantly, lit-html is FAST.
lit-html is not Virtual DOM… but better
By design, the template literal can be prefixed with a tag (did you open the MDN link above?).
A tag, in this case called html
is actually a function with a simple signature:
1
|
|
The strings will be an array of all static parts and the values are the interpolated expressions. The trick is that
whenever the render
function is called with the same template it will actually be just one instance (even if it’s) not
visible in code. lit-html takes advantage of that fact and whenever the same template is used, it will only update any
changed expressions.
In the example above each click will only update a tiny piece of the rendered HTML which will keep DOM operations to the
minimum. Even though the times
variable is calculated each time, it will only ever be rendered when it actually changes
between renders. Not every time.
So what about declarative UI?
Previously I struggled to bend <template>
to suit my needs in pursuit of a declarative solution for defining views
which are dynamically selected based on the content.
First of all, in my current implementation the order in which the templates appear in the page is important for the order in which the will be selected. In case there are multiple matches.
Secondly, I used Polymer 1.0’s Templatizer
which not only disappeared in Polymer 2.0, but it was also
notoriously buggy and hard to master.
With lit-html
I will be “freed” from Polymer and likely implement my elements will in plain JS. Additionally it will
be much easier to work with those templates; to extend with ES classes and compose with less custom element on the page.
Some sample code
At the top level would still be a view element
1 2 3 4 5 6 7 8 9 10 11 12 |
|
But from there on it could be all templates composed of smaller parts. Each part rendered with a template selected from
some templateRepository
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
The render
imported above would select a template from the repository and insert it into the parent template. No need
for nesting <object-view>
elements. That is of course if I figure out how to observe changes ;)
Bottom line
I really do like what’s coming with Polymer 3.0. It will embrace ES6 modules, finally. It may be that Polymer will become more similar to Vue or React but it will still be closest to the Web Platform.