Web Components Updates 2017

2017.11.15 html_modules_study

@1000ch

Web アプリケーション開発を専門とするソフトウェアエンジニア。企業で働く傍ら、技術顧問として複数企業のエンジニアリングに関わり、高品質で維持しやすい Web アプリケーションを作るための活動を続けている。

超速本 をよろしくおねがいします📕🙏

これから入るかもしれない仕様

※2017年11月現在、議論中の実験的な仕様です

Void or Self-closing Elements

カスタム要素で / の省略やセルフクロージングをできるようにする

カスタム要素で終了タグが必須な件

<!-- まぁわかる -->
<fancy-button>fancy button</fancy-button>

<!-- だがてめーは(ry -->
<fancy-input></fancy-input>

<!-- できればこう書きたい -->
<fancy-input />

<!-- あるいはこう書きたい -->
<fancy-input>

/ の省略やセルフクロージングの意義

<input><img> のように独立して意味を成すカスタム要素があるかもしれない

カスタム要素の長さに応じて冗長になる記述

<the-most-important-element><the-most-important-element><the-most-important-element /> と書けても良さそう

各所での議論

  • HTML Parser の挙動を変えるのは危険が伴う
  • カスタム要素名の制約にハイフン付きがあるなら大丈夫かも?
  • TPAC 2017 でどういう話になったのか気になる…

assignedElements()

<slot> 要素に挿入された要素を参照する

挿入された要素を Shadow DOM 内部で参照したい

<fancy-button>
  <the-icon></the-icon>
  Fancy Button
</fancy-button>

<the-icon>Fancy Button<fancy-button> のデフォルトスロットに挿入される

HTMLSlotElement#assignedNodes() は?

const button = document.querySelector('fancy-button');
const slot = button.shadowRoot.querySelector('slot');

// 要素だけでなくテキストノードも返る
console.log(slot.assignedNodes());

HTMLElement だけ取得したい === assignedElements()

HTMLSlotElement.prototype.assignedElements = function() {
  const nodes = this.assignedNodes();
  const elements = nodes.filter(function (node) {
    return node.nodeType === Node.ELEMENT_NODE;
  });

  return elements;
};

Custom Psuedo-elements

Shadow DOM 内の要素に任意の疑似セレクタを定義できるようにする

Custom Psuedo-elements のイメージ

<date-range-selector>
  <!-- #shadow-root -->
    <input pseudo="start-date" type="date">
    <input pseudo="end-date" type="date">
  <!-- /shadow-root -->
</date-range-selector>
<style>
  date-range-selector::start-date,
  date-range-selector::end-date {
    color: red;
  }
</style>

パーサーの振る舞いと相性が若干悪そうで、旗色が悪い?

CSS Shadow Parts

Shadow Host を ::part()::theme() で参照する CSS セレクタ。/deep/ やら ::shadow の登場によって削除された仕様だが、Custom Psuedo-elements によって復活?

CSS Shadow Parts のイメージ

<date-range-selector>
  <!-- #shadow-root -->
    <input part="start-date" type="date">
    <input part="end-date" type="date">
  <!-- /shadow-root -->
</date-range-selector>
<style>
  date-range-selector::part(start-date),
  date-range-selector::part(end-date) {
    color: red;
  }
</style>

HTML Modules

ES Modules を拡張して HTML ファイルをロードできるようにする

HTML Modules のイメージ

<!-- bar.html -->
<template>
  <div>Display!</div>
</template>

<!-- app.js -->
<script type="module">
  import bar from './bar.html';
  const template = bar.querySelector('template');
</script>

HTML Modules 所感(個人の感想です)

  • import/export で HTML をロードしていいのか
    • as によるキャスト前提なら大きな違和感はない
    • ECMAScript に足を踏み入れると広げた風呂敷が(ry
  • <script type="module" src=".html"></script> で HTML パーサー上のみで動いたほうがしっくり来る
    • 一周回って HTML Imports の方が良い気もしてくる🤔
  • とにかく宣言的に Web Components を使いたい

HTML Template Instantiation

template 要素に mustache の文法を取り込んで、テンプレートとしての機能を拡充する

HTML Template Instantiation

<template id="tmpl">
  <h1>{{name}}</h1>
  <a href="mailto:{{email}}">{{email}}</a>
</template>
<script>
  document.querySelector('#tmpl').createInstance({
    name: 'Shogo Sensui',
    email: 'shogosensui@gmail.com'
  }).update({
    name: '1000ch',
    email: 'shogosensui+1000ch@gmail.com'
  });
</script>

type (processor) の定義

<template type="tmpl-type">
  <h1>{{name}}</h1>
</template>
<script>
  document.defineTemplateType('tmpl-type', {
    processCallback: (instance, parts, state) => {
      for (const part of parts) {
        part.value = state[part.expression];
      }
    }
  });
</script>

<link rel="modulepreload">

ES Modules な JavaScript ファイルを先読みする

依存関係が深いとロードが遅くなる問題

rel="modulepreload" の使い方

<link rel="modulepreload" href="app.js">
<link rel="modulepreload" href="helpers.js">
<link rel="modulepreload" href="irc.js">
<link rel="modulepreload" href="fog-machine.js">

<script type="module" src="app.js"></script>

rel="preload"as="module" はダメ?

  • script 要素の credential と一致している必要がある
  • 事前に module なのかどうかわかっていないとパースに困る (v8?)
  • <link rel="preload"> は単一リソースをロードする仕様
  • 保存先が preload cache ではなく module map である必要がある (chrome?)

おしまい