2019年の正月に始めるwebcomponentとlit-html
他サイトに掲載していた過去記事サルベージ
以下本文
前置き
https://qiita.com/aggre/items/3ed2558a9fb8ba887385 こちらの記事を大いに参考にさせてもらいました。被っている内容もあります。 この分野周辺の変遷が早すぎるせいか変わっている部分がそこそこあったので、動かすための備忘録です。
コンポーネント全容
とりえあず動かせたコンポーネントの全体像。
- html側
<x-product name="JavaScript" src="imageSrc" />
- js側
import { html, render as litRender } from 'lit-html'
function render (h, c) {
return litRender(h, c.shadowRoot || c.attachShadow({ mode: 'open' }))
}
class XProduct extends HTMLElement {
static get observedAttributes () { return ['name'] }
constructor () { super() }
html (name, src) {
return html`<img alt="${name}" src="${src}" @click="${() => this.onClick()}" />`
}
connectedCallback () {
const name = this.getAttribute('name')
const src = this.getAttribute('src')
render(this.html(name, src), this)
}
attributeChangedCallback (attributeName: string, oldValue: string, newValue: string, namespace: string) {
console.log('attributeChangedCallback', attributeName, oldValue, newValue, namespace)
}
onClick () {
console.log(this.getAttribute('src'))
}
}
customElements.define('x-product', XProduct)
ポイント
constructor内でattributeは参照できない
2017年から既出っぽいが、webcomponentの使い方で調べるとconstructor
内でgetAttribute
している内容が多い。
何か歴史的変遷があったのかもしれない。とりあえず現時点では取得できない。
connectedCallback
内であれば取得できるのでそちらで初期化する。
connectedCallback () {
const name = this.getAttribute('name')
const src = this.getAttribute('src')
render(this.html(name, src), this)
}
https://stackoverflow.com/questions/42719914/custom-element-class-this-getattributedata-returns-null
イベントハンドラは@
html`<img alt="${name}" src="${src}" @click="${() => this.onClick()}" />`
以前 は拡張機能扱いでまだ仕様が揺れていたようだが、今は標準機能入りしてVue.jsっぽい定義方法になっている。
https://lit-html.polymer-project.org/guide/writing-templates#add-event-handlers
attributeChangedCallback
の監視対象は明示する必要がある
observedAttributes
というstatic
メソッドでreturn
された属性名のみがattributeChangedCallback
の監視対象となる。
マウント時点で監視対象属性に値が入っている場合、connectedCallback
より前にattributeChangedCallback
が実行される。
static get observedAttributes () { return ['name'] }
attributeChangedCallback (attributeName: string, oldValue: string, newValue: string, namespace: string) {
console.log(attributeName, oldValue, newValue, namespace)
}
shadow domの初期化は一度だけ
attachShadow
を2回実行するとエラーになるので、2回目移行はshadowRoot
を参照する必要がある。
先人に倣って関数に切り出したが、constructor
でattachShadow
しておき、後はshadowRoot
を使うという風にするだけでいいかもしれない。
function render (h, c) {
return litRender(h, c.shadowRoot || c.attachShadow({ mode: 'open' }))
}
おまけ
es5トランスパイルは不可
es2015のclass機能が必要なので、es5へトランスパイルしているとエラーになる。 es2015非対応ブラウザがwebcomponentに対応しているわけがないので潔く切る。
Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.