lit-htmlでHello World!, FizzBuzz!

![lit-html](https://qiita-image-store.s3.amazonaws.com/0/90678/e9907040-af42-d48a-4459-d65ba8477938.png)
<img src="https://www.polymer-project.org/images/logos/p-logo.png" width="37" height="26"> [Polymer](https://www.polymer-project.org/)の新しい描画エンジンが **次世代HTMLテンプレートライブラリ** と謳う [lit-html](https://polymer.github.io/lit-html/) になっているので勉強してみました。

## lit-htmlとは

 - サイズが小さく高速 → [JSベンチマーク](https://rawgit.com/krausest/js-framework-benchmark/master/webdriver-ts-results/table.html)
 - VirtualDOMを使わない
 - 2-way(双方向)bindingじゃない
 - HTML in JSで文字列テンプレートの中にJSを埋め込む
 - `html`タグリテラル関数でテンプレートをつくり、`render()`で描画。`directive()`でヘルパー関数をつくる
 - Webコンポーネントにするには[LitElement](https://github.com/Polymer/lit-element)として使うと便利

まあ、Chromeの開発チーム(ブラウザベンダ)がつくっているので、「DOMは遅いからVDOM」とか「JSの中にHTMLっぽいもの入れようぜ(JSX)」じゃなくてよいです。
ただ、2-way bindingに慣れていた身からするとReactぽい(?)描画や状態管理を勉強しなければならなさそうです、、、

## どう使うか

```html
<!doctype html>
<script type="module">
  import {html, render} from 'https://unpkg.com/lit-html?module'
  render(html`Hello World !`, document.body)
</script>
```

[:globe_with_meridians: デモサイト (plunker)](https://next.plnkr.co/edit/0zAUTc6czp4nApzY)

`type=module`、、、? ES Moduleということは[IE11で動かない](https://caniuse.com/#feat=es6-module)のでしょうが、いくつか公開されているlit-htmlを使っている[サンプルアプリ](https://polymer.github.io/pwa-starter-kit/sample-apps/)がIE11で動いているので大丈夫なのでしょう。

## `html`タグテンプレートリテラル

```js
html`Hello World !`
```

ES2015/ES6で追加された[Tagged Template literal](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings)が使用されていて、

```js
const hello = name => html`Hello ${name}`
render(hello('World'), document.body)
```

こんな感じで値を入れて描画することができます。

```js
const fizzBuzz = count => html`<ul>${[...Array(count)].map((_, i) => html`<li>${((i+1) % 3 == 0 ? 'Fizz' : '') + ((i+1) % 5 == 0 ? 'Buzz' : '') || (i+1)}</li>`)}</ul>`
render(fizzBuzz(20), document.body)
```

ループや条件式も入れることができます。

[:globe_with_meridians: デモサイト (plunker)](https://next.plnkr.co/edit/LwjKzrDZXTDITC17)

## `render`レンダラー

```js
render(html`hoge`, document.querySelector("#container"))
```

`html`で生成されたテンプレートは`render()`で指定されたDOM以下に展開されますが、これだけなら[こちらの記事](https://medium.com/@trukrs/tagged-template-literal-for-html-templates-4820cf5538f9)のように自分で拡張すれば済みそうです。
どうも[作者の記事](https://medium.com/@kennethrohde/a-bit-about-lit-html-rendering-2964c50ee56c)によると、内部で生成された`TemplateResult`オブジェクトに`Parts`オブジェクトで目印をつくっておいてDOMをキャッシュしながら部分的に置換していくのが、Appleから出てきた[Template Instantiation](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Template-Instantiation.md)の仕様提案と親和性があってよい! らしい。。。

![lit-html-parts](https://cdn-images-1.medium.com/max/800/1*8GM-Upo5DhNCLLaIhV5z3w.png)

## `directive`プラグイン的な

基本`html`と`render()`しかないので`directive()`という関数で拡張させます。以下の例は標準で[用意](https://polymer.github.io/lit-html/guide/writing-templates#directives)されている`when`と`repeat`です。
JSの評価式やループとの違いは上述の`Part`オブジェクトによって遅延評価されたり、DOMにキャッシュされるのが利点のようです。

```js
import {html, render} from 'https://unpkg.com/lit-html?module'
import {when} from 'https://unpkg.com/lit-html/directives/when?module'
import {repeat} from 'https://unpkg.com/lit-html/directives/repeat?module' 

const fizzBuzz = count => html`<ul>
  ${repeat(
     [...Array(count)].map((_, i) => i + 1),
     i=>i,
     i => html`<li>${
      when(i % 3 == 0 && i % 5 == 0, ()=>'FizzBuzz', ()=>when(i % 3 == 0, ()=>'Fizz', ()=>when(i % 5 == 0, ()=>'Buzz', ()=>i))
      )}</li>`
  )}</ul>`

render(fizzBuzz(20), document.body)
```

[:globe_with_meridians: デモサイト (plunker)](https://next.plnkr.co/edit/9eNHdRJ7NIgotS3P)

## 他

HTMLタグに対する属性(`attribute`)やスタイル、イベントリスナーへの登録などは書き方が[特殊](https://polymer.github.io/lit-html/guide/writing-templates#binding-types)みたいですが、次回 [LitElement](https://github.com/Polymer/lit-element) を勉強する時に見てみたいと思ってます。

(続き :arrow_right: [LitElement+TypeScriptでTodoリスト](https://qiita.com/howking/items/6f481263f84d607fe525))

## 参考URL

- [本家 - GitHub](https://github.com/Polymer/lit-html)
- [lit-html-examples](https://github.com/LarsDenBakker/lit-html-examples) いろいろなサンプル
- [awsome-lit-html](https://github.com/web-padawan/awesome-lit-html) lit-html関連のリンク集
- [lit-html Clock](https://codepen.io/justinfagnani/pen/mBQpqJ) SVGを書き換えていく例