index.html
<html>
<head>
<link rel="import" href="/components/hello-world.html?as=hoge-piyo">
</head>
<body>
<hello-world>動かない</hello-world>
<hoge-piyo>動く</hoge-piyo>
</body>
</html>
components/hello-world.html
<template>
<div id="border-div">
<p>Hello World!!!</p>
<slot></slot>
</div>
</template>
<script>
/*
# このコードの素晴らしい点
## A. グローバル汚染をしていないこと
正確にはcustomElementの名前を一つ取っているが、それは利用側でコントロール可能(後述)なので、汚染とは言わない。esmoduleと同じ。
本当に何一つ汚染していない。クラス定義ですらも!
## B. 利用側のimportの仕方に依存していない
ownerDocumentを用いているので、利用側で、どのDomにHTMLImportされているのか、Component側は意識していない
## C. コンポーネント名を強制させない
デフォルトのコンポーネント名としてhello-worldを提供しているがそれだけである。
利用側が自由に名前をつけることができる。
他にも同盟のコンポーネントがあっても全く問題がない
*/
{
const ownerDocument = document.currentScript.ownerDocument;
const moduleName = new URL(document.currentScript.ownerDocument.URL).searchParams.get('as') || "hellow-world"
window.customElements.define(moduleName, class extends HTMLElement {
constructor() {
super()
const shadowRoot = this.attachShadow({ mode: 'open' })
const t = ownerDocument.getElementsByTagName('template')[0];
const instance = t.content.cloneNode(true);
shadowRoot.appendChild(instance);
}
});
}
</script>
HTMLImportsを使ったときの悩み
- customElement名が被ったら(同名のコンポーネントがあったら)死ぬ
- → importじのURLパラメータでcomponent名を渡すことで解決
- scriptからtemplateを参照するのに、documentから探索するのでは、コンポーネントに利用側の都合が発生して辛い
- → ownderDocumentを用いることで解決
- ownerDocumentはコールバック(constructorのこと)では使えないので、定数に格納する必要があるが、また一つグローバル汚染が増える
- esnextのスコープ構文でくるむことで解決
- ownerDocument等をスコープに隔離するとクラスで読めないし、クラスもスコープに入れると外部から参照できないジレンマ
- → クラスを外部公開せず、直接customElement.defineにわたすことで解決
- クラス定義自体が一つグローバル汚染になる
- → クラスを外部公開せず、直接customElement.defineにわたすことで解決
感想
HTMLImportsの辛い部分をほぼ払拭できたのではないだろうか。
私が見た記事の中で、この自分のコードが一番筋が良いと思う。
全世界に広まり、HtmlImports自体も復権することを願う。1
-
本当に残念なことに、HTMLImportsはWebComponentsの中で非推奨になってしまい、今はesModuleで読み込むのが主流になっているみたいだ。最悪である。なぜなら、jsの中でhtmlをベタ書きしなくてはならないから。WebComponentsがただの改悪版Reactに成り下がったのである。(Mozillaが実装しなかったかららしい?)ともかくいくら残念とはいえHTMLImportsはスタンダードではない。 ↩