Ionicのコードリーディングしてて気になったので、stencilのHost要素の役割をまとめる

公開:2020/12/22
更新:2020/12/22
2 min読了の目安(約2300字TECH技術記事

Ionicのコード(Stencil)を読んでいると、JSXとしてrenderされる要素はかならず Host 要素でくくられています。たとえば、IonItemの場合、以下のような形です。

https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.tsx#L277-L317

なので、てっきり Host は、Web Componentsの host を示していて必須要素だと思ってたのですが、そうでないようなので役割をまとめようと思います。

Hostの役割

host 要素がどのように動いてるかはドキュメントから読むことができます。

https://stenciljs.com/docs/host-element

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 をつけて実装するようにすることがおすすめです。

それではまた。