f:id:seeds-std:20191024163712p:plain

こんにちは、プログラマーの衣笠です。
Web Componentsの各ブラウザでの実装がそろってきたのでそろそろ触ってみようと調べていたら、open-wcというものを見つけました。ツールを用意してくれていたので今回はこれを使って簡単なコンポーネントをひとつ作ってみたいと思います。

open-wc とは

Web Componentsを作成、共有する上での推奨事項をまとめてそれをツールとして提供することを目的とした集まりです。

open-wc.org

推奨事項は

  • Developing
  • Linting
  • Testing
  • Building
  • Demoing
  • Publishing
  • Automating

と章をわけてガイドを提供してくれています。
そこで推奨しているパッケージや設定を用意してくれていてツールで生成できるようになっています。

open-wcのツールで開発環境構築

ツールを使うと簡単に開発環境を用意してくれます。
npmが使えてプロジェクトのディレクトリを置きたい場所で

npm init @open-wc

とすると勝手にツールをダウンロードしてきて実行されます。
ツールが実行されると色々質問されます。作成されるものはその回答によって変わります。
今回は簡単は単一のコンポーネントのプロジェクトを作成したいと思うので以下のように回答すると

What would you like to do today? › Scaffold a new project
What would you like to scaffold? › Web Component
What would you like to add? › (選択なし)
What is the tag name of your application/web component? … password-input
Do you want to write this file structure to disk? › Yes
Do you want to install dependencies? › Yes, with npm  

password-input ディレクトリを以下のような構成で作成してくれます。

.
├── LICENSE
├── README.md
├── demo
│   └── index.html
├── index.js
├── package-lock.json
├── package.json
├── password-input.js
└── src
└── PasswordInput.js

テストやビルドの環境などをあとで追加したい場合、もう一度ツールを使って追加できます。

開発サーバー

ツールで生成されたディレクトリで npm run start をすると開発サーバーが立ち上がってブラウザで 自動でdemo/index.html を開きます。
開発サーバーは es-dev-server というopen-wc お手製のパッケージが使われています。
open-wcはできるだけWeb標準に近づくことで長期投資をする方針で、開発も最新のブラウザでES Modulesを活用して進めることを推しています。なので es-dev-server はビルドツールを使わない前提の仕様になっています。
ビルドの待ち時間がないっていうのはすごい魅力的ですね。
また、node_modules のパッケージもいい感じに読み込んでくれます。

WebComponentの実装

今回は最近よくある入力したパスワードの内容を見えるように切り替えられるinput要素 password-input を作ってみました。
作成したものをGlitchで埋め込んでおきます。View Appボタンでデモページに切り替えられます。

実装は src/PasswordInput.js にしていきます。demo/index.html はデモページです。
open-wcはLitElementとlit-htmlを使った実装を推奨していて、プロジェクト生成時点で使えるようになっています。
ここからはLitElementでの実装方法を説明していきます。

カスタム要素の定義

カスタム要素は通常HTMLElementクラスを継承したクラスを定義しますが、LitElementではLitElementクラスを継承して定義します。
タグ名とクラスの紐付けは password-input.js で既にされているので触る必要がありません。

要素のプロパティ、属性

LitElementではゲッター properties で返すオブジェクトで要素のプロパティを定義します。

  ...
static get properties() {
return {
value: {type: String},
visibility: {type: Boolean}
};
}
constructor() {
super();
this.value = "";
this.visibility = false;
}
...

オブジェクトのプロパティに定義したいプロパティの名前、その値のオブジェクトに設定を指定します。
基本的に定義したプロパティと同じ名前の属性も一緒に定義されます。
typeBoolean にすると disabled 属性みたいな扱いになります。
初期値はコンストラクタで設定できます。

要素のコンテンツ

Web Componentsはカスタム要素のコンテンツをShadowDOMによって定義します。
LitElementでは renderメソッドに定義していきます。

  ...
_toggleVisibility() {
this.visibility = !this.visibility
}
render() {
return html`
<input type=${this.visibility ? 'text' : 'password'}
value=${this.value}
>
<span
class="toggle"
@click=${this._toggleVisibility}
>
....
</span>
`;
}
...

render メソッドはlit-elementが用意しているタグ付きテンプレートリテラルの html を使ってコンテンツを定義します。
イベント名に接頭辞 @ を付けた属性にイベントハンドラをバインドできます。

要素のスタイル

要素のスタイルの定義は styles ゲッターに定義します。LitElementが用意している css タグ付きテンプレートリテラルを使います。
コンテンツのスタイルはShadowDOMの中に定義されてカプセル化されるので外の要素に影響しません。Vue.jsのscoped CSSと同じです。
CSSの管理が楽になるのでありがたいです。

  ...
static get styles() {
return css`
:host {
--icon-size: 24px;
display: inline-flex;
...
}
...
input {
line-height: 1em;
...
}
...
.icon {
width: var(--icon-size);
height: var(--icon-size);
}
...
`;
}
...

:host セレクタでカスタム要素のスタイルを定義します。カスタム要素のスタイルは外から変更可能です。
対してそれ以外のShadowDOMのスタイルは外から直接変更できません。
外から変更する方法のひとつにCSSカスタムプロパティを使った方法があります。
ここでは --icon-size を定義して外からパスワードの変更を切り替えるボタンのアイコンのサイズを変更できるようにしています。
他にも Shadow Parts という part 属性と ::part() セレクタを使った方法があります。

::part() – CSS: Cascading Style Sheets | MDN

まとめ

以上がopen-wcで簡単なWeb Componentをつくってみた内容になります。

今回はDevelopingのみを扱いましたが、簡単なものだったので紹介できていないこともあります。興味があればぜひ公式のガイドを読んでみてください。
文章では伝わらなかったと思いますがビルドの時間がないというのはすごく快適です。ES Modulesが普及してきたからこそだと思いますが、Web Componentsの開発環境だけではなく他の開発環境でもこのスタイルが広まればいいなと思いました。
open-wcも今年できたばかりで日本語の情報がほとんどありませんがこの記事がみなさんの触れるきっかけになれば幸いです。