Ionicのコードリーディングしてて気になったので、stencilのHost要素の役割をまとめる
Ionicのコード(Stencil)を読んでいると、JSXとしてrenderされる要素はかならず Host
要素でくくられています。たとえば、IonItemの場合、以下のような形です。
なので、てっきり Host
は、Web Componentsの host
を示していて必須要素だと思ってたのですが、そうでないようなので役割をまとめようと思います。
Hostの役割
host
要素がどのように動いてるかはドキュメントから読むことができます。
Host
はVirtual APIであり、DOMとしてレンダリングされることはありません。レンダリングの時にハンドリングを行ったり、内部APIの起点をつくるために利用されます。
Web ComponentsのHost操作
例えば、以下のようなWeb Componentsを定義してたとします。ここでは定義名は todo-list
としましょう。
@Prop() open = false;
...
<Host
aria-hidden={this.open ? 'false' : 'true'}
class={{
'todo-list': true,
'is-open': this.open
}}
/>
このWeb Componentsを利用する時は、 open
属性を持ちますので、 <todo-list open="true">
のように利用することができます。しかし、 open
属性で、Host自身の area-hidden
とclassが変化するようになっていますので、実際は以下のようにレンダリングされます。
<todo-list open="true">
↓
<todo-list class="todo-list is-open" aria-hidden="false"></todo-list>
<todo-list open="false">
↓
<todo-list class="todo-list" aria-hidden="true"></todo-list>
Web Components自身のレンダリングを変化させるときにとても有用ですね。
トップレベルDOMとして
Web ComponentsはComponentsですので、トップレベルDOMを複数もつことはできません。つまり、以下のようなことはできません。
return (
<h1>Title</h1>
<p>Message</p>
);
ここで不要な div
などを使って、ネストを深くする必要性はありません。Hostを使いましょう。
return (
<Host>
<h1>Title</h1>
<p>Message</p>
</Host>
);
Component内のAPIとして
Host
を利用していれば、Web Componentsをトップとして @Element()
デコレーターを使うことができます。
import { Element } from '@stencil/core';
...
export class TodoList {
@Element() el: HTMLElement;
getListHeight(): number {
return this.el.getBoundingClientRect().height;
}
}
いや、 Host
を使ってなくても、 @Element()
は使うことはできるといえばできるのですが、状態の更新( prop
など)を動的にとれなくなりAPIとして安定しないので、 Host
を使いましょう。
Shadow DOMのセレクターとして
以下のようなスタイリングをあててたとします。
my-element {
color: black;
}
my-element div {
background: blue;
}
これはShadow DOMを有効にすると、セレクターの起点が変わるため、要素自身にスタイリングを指定する場合は Host
を利用している必要があります。 Host
を利用していると、Shadow DOMを有効にした場合、以下のように書き直すことができます。
:host {
color: black;
}
div {
background: blue;
}
Host
を利用していない場合、要素自身にスタイリングをあてることはできません。
まとめ
div
だと Host
の威力はわかりにくいですが、既存のHTMLElementを踏襲・拡張する時などは Host
がないと実装が不可能な場合もあるので、とりあえず Host
をつけて実装するようにすることがおすすめです。
それではまた。
Discussion