Smart Communication Design Company
ホーム > ナレッジ > Blog > フロントエンドBlog > 2019年10月 > StencilでかんたんにWeb Componentsを実装する

StencilでかんたんにWeb Componentsを実装する


UI開発者 古川

Stencilは、Ionic Frameworkチームが開発したWeb Componentsコンパイラです。

開発環境にTypeScript、JSX、VirtualDOM、データバインディング、非同期レンダリングなどのWeb開発技術を網羅しており、ビルド後はWeb標準に準拠したWeb Componentsのソースコードを生成してくれます。

今回はStencilの導入方法を紹介します。

Stencilのインストール

適当なディレクトリで以下のコマンドを実行しましょう。

 npm init stencil

そうするとStencilが提供するスターターの選択肢がコマンドラインに表示されます(コマンドを実行するにはnpm v6以上が必要です)。

? Pick a starter » - Use arrow-keys. Return to submit.

   ionic-pwa     Everything you need to build fast, production ready PWAs
   app           Minimal starter for building a Stencil app or website
>  component     Collection of web components that can be used anywhere

今回はcomponentを選択します。

componentStencil Component Starterの環境をセットアップしてくれます。Stencil Component StarterはStencilでWeb Componentsを開発するスタンドアロン型の開発環境です。 ほかにもStencilでは、PWA Toolkitが導入済のスターターや、アプリケーション構築に必要な環境が整っているStencil App Starterを提供しています。

componentを選択すると、続いてコマンドライン上でアプリの名前を要求されます。

√ Pick a starter » component
? Project name » my-components

任意の名前を設定するとフォルダが生成されるため、生成されたディレクトリに移動しnpm startを実行します。

cd my-components
npm install
npm start

以下のキャプチャのように、ブラウザでローカルサーバーが立ち上がったら成功です。

Stencil Component Starterを立ち上げたのちにブラウザでローカルサーバーにアクセスした際のキャプチャ

コンポーネントの追加

Stencil Component Starterはローカルサーバーやテストの設定ファイル、およびサンプルのコンポーネントを含む開発環境を提供します。インストールしたばかりのスターターは開発環境の設定ファイルの他に、コンポーネントなどのリソースを格納するsrcディレクトリが存在します。ローカルサーバーを立てたりビルドを実行すると、ドキュメントルートにwwwディレクトリとdistディレクトリが生成されます。wwwディレクトリにはWebサイトでコンポーネントを表示するためのリソースが、distディレクトリには再利用可能なライブラリとしてコンポーネントを利用するためのスクリプトがそれぞれ格納されます。

以下はビルド後のStencil Component Starterのファイル構成のキャプチャです。

設定ファイルの他にsrcディレクトリ、wwwディレクトリ、distディレクトリがドキュメントルートに含まれているStencil Component Starterのファイル構成のキャプチャ

StencilのコンポーネントはJSXとTypeScriptで作成するため、Web Componentsを追加する場合はsrc/componentsディレクトリに.tsxファイルを追加します。

今回はsrc/components配下にmy-custom-buttonという名前のディレクトリを作成し、my-custom-button.tsxを格納しました。

my-custom-button.tsxの内容は以下です。

import { Component, Prop, State, h } from '@stencil/core';

// @Component: コンポーネントの宣言
@Component({
  tag: 'my-custom-button'
})
export class MyCustomButtonComponent {
  // @Prop(): プロパティや属性の宣言
  @Prop({ mutable: true }) text: string = 'ボタン';

  // @State(): コンポーネントの内部状態の定義
  @State() checked: boolean;

  // @Event(): コンポーネントが発行するDOMイベント
  toggleChecked() {
    this.checked = !this.checked;
  }

  // render(): 
  // 実行時にDOMにレンダリングされるコンポーネントを返す関数
  render() {
    return (
      <button onClick={() => this.toggleChecked()} 
        class={(this.checked && "checked")}>
        {this.text}
      </button>
    );
  }
}

上記で記載したほかにも、Stencilではデコレーターやライフサイクルフックを提供しています。詳細はドキュメントを参照してください。

コンポーネントを登録したら、src/index.html<my-custom-button>要素を追記します。

<my-custom-button text="ボタンテキスト"></my-custom-button>

ここまでの表示は以下のキャプチャのようになります。

コンポーネントをHTMLに追加したのちブラウザで表示を確認したキャプチャ

スタイルを適用する

CSSを追加するには@ComponentsstyleUrlsプロパティを追加し、外部ファイルとしてCSSファイルを作成します。Shadow DOMはデフォルトで有効になっていないため、利用したい場合は@Componentsshadow:trueを設定します。

@Component({
  tag: 'my-custom-button',
  styleUrl: 'my-custom-button.css',
  shadow: true
})

今回はsrc/components/my-custom-buttonディレクトリにmy-custom-button.cssを作成し、以下のようなCSSを記載しました。

button {
  background-color: skyblue;
  border-radius: 4px;
  cursor: pointer;
  display:inline-block;
  padding: 5px 10px;
  text-align: center;
}

スタイルを付与した状態は以下です。

コンポーネントにスタイルを追加したのちブラウザで表示を確認したキャプチャ

テストする

Stencilはデフォルトで単体テストとE2Eテストを実行できます。どちらのテストもJestが採用されており、E2EテストではPuppeteerが利用されています。

テストの実行にはsrc配下にテストファイルを作成します。

今回はsrc/components/my-custom-button配下にmy-custom-button.e2e.tsxというファイルを作成し、作成したコンポーネントが存在するかをチェックするテストを実行してみました。

import { newE2EPage } from '@stencil/core/testing';

describe('my-custom-button', () => {
  it('renders', async () => {
    const page = await newE2EPage();
    await page.setContent('<my-custom-button></my-custom-button>');
    const element = await page.find('my-custom-button');
    expect(element).not.toBeNull();
  });
});

コマンドは以下のコマンドを実行します。

npm run test

実行結果は以下のキャプチャです。

テストの実行結果を表示したコマンドプロンプトのキャプチャ

ビルドする

Stencilではコンポーネントの使用方法に応じて4種類のビルド方法を設定できます。

stencil.config.tsという名前の設定ファイルがスターターのドキュメントルートにあるので、そのファイルのoutputTargetsを上記のいずれかに設定します。デフォルトではwwwです。

import { Config } from '@stencil/core';

export const config: Config = {
  outputTargets: [
    {
      type: 'dist'
    },
    {
      type: 'www'
    }
  ]
};

設定を行ったら、以下のコマンドを実行します。

npm run build

今回はdocs-readmeを設定し、ビルドしてマークダウン形式のドキュメントを出力したいと思います。コマンドを実行すると、src/components/my-custom-button配下にreadme.mdという名前のマークダウンファイルが生成されました。

内容は以下です。

# my-custom-button

<!-- Auto Generated Below -->

## Properties

| Property | Attribute | Description | Type     | Default    |
| -------- | --------- | ----------- | -------- | ---------- |
| `text`   | `text`    |             | `string` | `'ボタン'` |

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*

まとめ

Stencilがサポートするブラウザは以下です:

EdgeやIEではWeb Componentsの一部機能をポリフィルで提供しますが、モダンブラウザと呼ばれる環境で動作するため、実案件でも十分導入が可能そうです。

インストールするだけで開発からテスト、ビルドまで柔軟に対応してくれるため、Stencilは非常に便利なWeb Componentsコンパイラだと感じました。要件が合えばぜひ実案件でも取り入れたいです。