lit-element で React 風に要素渡し
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ lit-element でも React 風にプロパティで要素を渡せる
◆ lit-html の html タグ関数の返り値はテンプレートオブジェクトなので正確にはテンプレートを渡してる
◆ slot と同じようなことができるけど CSS のスコープに違いがある
◆ slot は親側のスタイルが適用されるけど テンプレート渡しだと子側のスタイルが適用される
◆ lit-html の html タグ関数の返り値はテンプレートオブジェクトなので正確にはテンプレートを渡してる
◆ slot と同じようなことができるけど CSS のスコープに違いがある
◆ slot は親側のスタイルが適用されるけど テンプレート渡しだと子側のスタイルが適用される
前に React で slot を使いたいという記事を書きましたが React だと基本は props で渡すことになります
WebComponents だと ShadowDOM の機能で slot が使えるのですが lit-element で React 風にやってみました
似た感じで扱えます
渡してる部分はここです
html タグ関数でテンプレートを作って渡します
lit-html の html タグ関数で作られるものはあくまでテンプレートで HTML 要素の実体ではないです
child-elem 要素で受け取って render 内に ${} で埋め込みます
ボタンを押すと h1 の中の要素が変わって child-elem の中に反映できてます
slot は単純のようで結構複雑でもあるので React に慣れてるとこっちほうがシンプルで良いかもしれません
しかし WebComponents の場合 ただの渡し方の違い以外に大きめな影響のある違いがあります
比較用の例を用意しました
child-elem1 はプロパティで渡したもので child-elem2 は slot で渡したものです
両方とも h1 要素を渡していて parent-elem では h1 に文字色を付けています
child-elem では両方で h1 の背景色を付けています
プレビューしてみるとわかりますが child-elem1 では背景色がついて文字色はついていません
child-elem2 では文字色がついて背景色はついていません
プロパティで渡した場合 その要素は子コンポーネント内で作られるので子コンポーネントのスタイルが適用されます
それに対して slot を使うと親コンポーネントのスタイルが適用されます
React なら親側で要素の className を設定しておけば コンポーネントごとにスタイルを管理していても 親側のスタイルを渡した要素に適用できます
ですが ShadowDOM を使うと ShadowRoot ごとに読み込んでいるスタイルシートが違うので WebComponents で同じことをしても意味がないです
渡した要素に適用するためのスタイル定義も別に渡して 子コンポーネント側のスタイルシートに追加するか style 属性をつけた要素を渡す必要があります
コンポーネントを使う側が 内側に表示するコンポーネントのスタイルも指定することが多いですし 上のような回避策はイマイチだと思うので 基本は slot の方が良さそうです
ですが 子コンポーネント側のスタイルを使ってほしいこともあるので そういうときはプロパティ渡しが使えそうです
内部で自身のスタイル定義を持ってる custom element を渡す場合はこの問題の影響はほぼないので好きな方で問題なさそうです
WebComponents だと ShadowDOM の機能で slot が使えるのですが lit-element で React 風にやってみました
似た感じで扱えます
<!doctype html>
<script type="module">
import { html, css, LitElement } from "https://unpkg.com/lit?module"
customElements.define("child-elem", class extends LitElement {
static get properties() {
return {
elem: { type: Object },
}
}
static get styles() {
return css`.bordered { border: 1px solid royalblue; }`
}
constructor() {
super()
this.elem = null
}
render() {
return html`
<div class="bordered">${this.elem}</div>
`
}
})
customElements.define("parent-elem", class extends LitElement {
static get properties() {
return {
msg: { type: String },
}
}
constructor() {
super()
this.msg = "A"
}
render() {
return html`
<div>
<button @click=${() => this.msg = "A"}>A</button>
<button @click=${() => this.msg = "B"}>B</button>
<button @click=${() => this.msg = "C"}>C</button>
</div>
<child-elem
.elem=${html`<h1>${this.msg}</h1>`}
></child-elem>
`
}
})
</script>
<parent-elem></parent-elem>
渡してる部分はここです
<child-elem
.elem=${html`<h1>${this.msg}</h1>`}
></child-elem>
html タグ関数でテンプレートを作って渡します
lit-html の html タグ関数で作られるものはあくまでテンプレートで HTML 要素の実体ではないです
child-elem 要素で受け取って render 内に ${} で埋め込みます
render() {
return html`
<div class="bordered">${this.elem}</div>
`
}
ボタンを押すと h1 の中の要素が変わって child-elem の中に反映できてます
slot は単純のようで結構複雑でもあるので React に慣れてるとこっちほうがシンプルで良いかもしれません
しかし WebComponents の場合 ただの渡し方の違い以外に大きめな影響のある違いがあります
スタイルのスコープ
slot を使うかどうかで CSS のスコープが変わります比較用の例を用意しました
<!doctype html>
<script type="module">
import { html, css, LitElement } from "https://unpkg.com/lit?module"
customElements.define("child-elem1", class extends LitElement {
static get properties() {
return {
elem: { type: Object },
}
}
static get styles() {
return css`
.bordered { border: 1px solid royalblue; }
h1 { background: pink }
`
}
constructor() {
super()
this.elem = null
}
render() {
return html`
<div class="bordered">${this.elem}</div>
`
}
})
customElements.define("child-elem2", class extends LitElement {
static get styles() {
return css`
.bordered { border: 1px solid royalblue; }
h1 { background: pink }
`
}
render() {
return html`
<div class="bordered"><slot></slot></div>
`
}
})
customElements.define("parent-elem", class extends LitElement {
static get styles() {
return css`h1 { color: red }`
}
render() {
return html`
<child-elem1
.elem=${html`<h1>1</h1>`}
></child-elem1>
<child-elem2>
<h1>2</h1>
</child-elem2>
`
}
})
</script>
<parent-elem></parent-elem>
child-elem1 はプロパティで渡したもので child-elem2 は slot で渡したものです
両方とも h1 要素を渡していて parent-elem では h1 に文字色を付けています
child-elem では両方で h1 の背景色を付けています
プレビューしてみるとわかりますが child-elem1 では背景色がついて文字色はついていません
child-elem2 では文字色がついて背景色はついていません
プロパティで渡した場合 その要素は子コンポーネント内で作られるので子コンポーネントのスタイルが適用されます
それに対して slot を使うと親コンポーネントのスタイルが適用されます
React なら親側で要素の className を設定しておけば コンポーネントごとにスタイルを管理していても 親側のスタイルを渡した要素に適用できます
ですが ShadowDOM を使うと ShadowRoot ごとに読み込んでいるスタイルシートが違うので WebComponents で同じことをしても意味がないです
渡した要素に適用するためのスタイル定義も別に渡して 子コンポーネント側のスタイルシートに追加するか style 属性をつけた要素を渡す必要があります
コンポーネントを使う側が 内側に表示するコンポーネントのスタイルも指定することが多いですし 上のような回避策はイマイチだと思うので 基本は slot の方が良さそうです
ですが 子コンポーネント側のスタイルを使ってほしいこともあるので そういうときはプロパティ渡しが使えそうです
内部で自身のスタイル定義を持ってる custom element を渡す場合はこの問題の影響はほぼないので好きな方で問題なさそうです