自作Web Components公開までの道


今月のHTML5のイベントで喋る題材としてPolymerとWeb Componentsについてお話をしようかと思っているので、お遊びで作ってみました。

left-swipe-actionというiOSのリストで左へスワイプすると後ろにActionボタンが現れるあれみたいなやつです。

画面イメージ1
スクリーンショット 2014-08-07 12.10.16

画面イメージ2
スクリーンショット 2014-08-07 12.04.19

Demoページ
http://tejitak.github.io/left-swipe-action/demo.html
(注:Polymerがサポートしていないブラウザでは見れないため、Android標準のブラウザ等では見れないです)

以下、初めてWeb Componentsを作ってみるところから、bowerやgalleryサイトへの登録までの流れをざっと紹介します。


なぜWeb Componentsを作って公開するのか

そもそもですが、Web Componentsを作って公開する意義はなんでしょうか。

使ってみるとわかるのですが、Web Componentsを使う側はめちゃくちゃラクです。ただ、HTMLのタグを埋め込むだけ。

これまで個人の開発者が作ってきたモジュールは、例えばjQueryのプラグインだとかAngularのディレクティブとかありますが、それらは基本的に知識のある技術者しか使えません。でも、Web Componentsの場合はただlinkタグで読み込んで、HTMLのマークアップをするだけなのでモジュールを「使う」という面での敷居が断然低くなります。

Web Componentsのギャラリー覗いて欲しいもの見つけたら配置して、マークアップのエンジニアさんやおそらくデザイナーさんでも、機能的に動く凄いものを作れてしまう時代がくるでしょう。つまり、これまでの公開されているJS/CSSモジュール等と違って、開発して公開したら使ってもらえる潜在的なユーザー層が爆発的に多くなるということです。

今後、開発者たちが作ったものをどんどん世の中に発信して良いcomponentsができてくれば自然と使う側も増え、良いエコシステムが出来上がってきそうな予感がしてます。

既にGalleryサイトもあります。
Custom Elements A Web Components Gallery for Modern Web Apps
http://customelements.io/

Web Components Catalog – Component Kitchen
http://component.kitchen/

(注:現時点ではComponent Kitchenはchromeでしか動いていないようです。)

ゆくゆくはWeb Componentエンジニア(作る側)とマークアップによるアプリケーション作成エンジニア(使う側)と大きく職種としても分かれる可能性もありますね。そんな世の中が来る日もそう遠くない気がしますので、今Web Componentsを作って公開することは開発者が名を売るチャンスでもあるし、間違いなくこれから大きな変化が訪れるであろうWebの世界のムーブメントに乗っていくために重要なことだと思います!←大げさ?w

長い前置きはさておき、以下、ざっと初めて作って公開してみた流れを記述。


目次

1. Polymerを勉強する
2. Web Componentsを実装する
  2-1. タグ名を決める
  2-2. Componentが受け取るattributeを決める
  2-3. Light DOMを想定する
  2-4. Shadow DOMを生成する
  2-5. テスト
3. Github.ioでデモを動かす
4. Readmeをしっかり書く
5. Bowerに登録
6. Galleryサイトに登録されているか確認


1. Polymerを勉強する

PolymerはGoogleが作ってるWeb ComponentのPolyfill(ネイティブ実装がされる前の互換実装)&フレームワークです。Polymerの登場により急速にweb componentが普及し始めてきた感じがしています。

今回はpolymerを使用して実装しますが、実はWeb Componentsを実装するのにPolymerは必須ではありません。もっと軽量でサポートブラウザが多いdocument-register-element.jsというPolyfillも存在します。

document-register-element.jsは、Web Componentsを作るための最低限のpolyfillで、Polymerのような統合的にweb componentを実装するためのフレームワークのようなものでは無さそうです。

Polymerの方は、コミュニティの巨大さや活発さはやはり凄いです。そこそこチュートリアル的な記事も出回り始めてます。ただ、やはりPolymer公式のドキュメントが一番役に立つと思います。

stackoverflowGoogle group polymer-devでは色々な質問回答が活発に行われています。

今回はPolymerを使ってweb componentを作ってみました。
Polymerは大きく2つの要素に分かれます。

Platform.js
Web ComponentsのPolyfill

Polymer.html
Web Componentsを作るためのフレームワーク

自分もまだPolymerが提供している機能とWeb Componentsの仕様としての機能を完璧に区別できている訳ではないですが、とりえあえず習うより慣れろの精神で作ってみています。

基本はplatformとpolymerを以下の様に読み込みます。

    <script src="bower_components/platform/platform.js"></script>
    <link rel="import" href="bower_components/polymer/polymer.html">

自作コンポーネントは、まずは依存web componentsを読み込み、以下の様にCSS + HTML + JSを一つのファイルに記述します。

<!--
My Web Components Description
-->
<polymer-element name="my-component">
    <template>
        <!-- My CSS here -->
        <style>
        </style>
        <!-- My HTML here -->
        <div>
            <content></content>
        </div>
    </template>
    <!-- My JS here -->
    <script>
        Polymer({});
    </script>
</polymer-element>

2. Web Componentsを実装する

今回の記事は、Web Componentsを作って公開するための概要説明なので、コードの詳細解説はスキップです。興味ある方は是非githubのリポジトリのチェックをお願いします。

pointereventsの使い方だとかのコードの詳細は今後の記事、もしくは会社のエンジニアブログでしっかりと詳細を書きたいなぁと思っています。

2.1 タグ名を決める

タグ名はかなり重要です。基本は-(ハイフン)区切りでわかりやすい名前を付けていくのですが、あまりgeneral過ぎても競合するし、長過ぎても使いづらいです。ざっとgallery等見てみると、大体xxx-xxxとかxxx-xxx-xxxとか、せいぜいハイフンを2つくらい付けていますね。(早いもん勝ちなんで、今は短い名前が多いだけかもしれないですが。)
会社や組織が作る場合はgoogle-xxxの例みたいに、{compony_name}-xxxとなりそうです。

今回は左スワイプでActionを表示させるものなので、そのままleft-swipe-actionにしましたw
タグ名はそのまま自作Web Componentsのファイル名になります(left-swipe-action.html)。

2.2 Componentが受け取るattributeを決める

Web Componentを定義する時にパラメーターを渡すには基本的にはHTMLのattributeを使用します。
今回の場合は以下の様にしました。

(Readmeから転載)

Name Type Default Description
open boolean false If true, the content will be opened by default
nodrag boolean false If true, drag (touch move) action is disabled and a content is automatically opened by click/touch
offset number 60 Offset pixcel position for opened state
shadow boolean false If true, box shadow is added

attributeを受け取る場合は、自作Web Componentファイルの先頭のpolymer elementの宣言時に明記します。

JS側で扱うための宣言は以下の様にします。

    Polymer({
            /**
             * If true, the content will be opened by default
             *
             * @attribute open
             * @type boolean
             * @default false
             */
            open: false,

            /**
             * If true, drag (touch move) action is disabled and a content is automatically opened by click/touch
             *
             * @attribute nodrag
             * @type boolean
             * @default false
             */
            nodrag: false,

            /**
             * Offset pixcel position for opened state
             *
             * @attribute offset
             * @type number
             * @default 60
             */
            offset: 60,

なるべく上記のコメントは書きましょう。
上記の形式で書いておくと、ギャラリーに登録した際に、自動的に整形してテーブル形式で表示してくれます。

2.3 Light DOMを想定する

Shadow DOMとはWeb Componentsの解釈後に生成するブラウザが本当のレンダリングするDOMです。一方、Light DOMはShadow DOMに対比して名付けられている使う側が定義時に記述するHTMLノードのことです。

今回の例では以下の様にLight DOMを想定します。

<left-swipe-action touch-action="pan-y">
    <div>
        <!-- 
          Your content to be swiped here
        -->
    </div>
    <left-swipe-action-button>
      <!-- 
        Your action here
      -->
    </left-swipe-action-button>
</left-swipe-action>

例: スワイプした時にPaperエレメントのボタンを2つ表示

<left-swipe-action touch-action="pan-y" shadow>
    <div style="padding: 18px;">
        <p>See how many actions are behind<br><strong>Swipe me to left</strong></p>
    </div>
    <left-swipe-action-button onclick="alert('delete')"><paper-fab icon="delete"></paper-fab></left-swipe-action-button>
    <left-swipe-action-button onclick="alert('star')"><paper-fab icon="star"></paper-fab></left-swipe-action-button>
</left-swipe-action>

Light DOMは上記の様にユーザーが定義するHTMLを表すもので、DOM形式でShadowに渡せる引数のようなものと考えて良さそうです。今回は自作タグの中に含まれている一つ目のdivをそのままスワイプ用のコンテナに入れて、left-swipe-action-buttonで記述されたものはスワイプされると表示されるnodeと想定しています。

2.4 Shadow DOMを生成する

ここがWeb Componentsの実装のメインです。

受け取ったLight DOMはWeb Componentsが処理される時に、Shadow側でタグを用いて任意の場所に埋め込むことができます。

今回のタグの使用例

        <div class="swipe-underlay" layout horizontal reverse>
            <content select="left-swipe-action-button"></content>
        </div>
        <div id="content" class="swipe-content open-{{open}}">
            <content></content>
        </div>

一つ目のcontent select=”left-swipe-action-button”により、light DOM上のleft-swipe-action-buttonタグがこのcontentタグと置き換えられてShadow DOMが作られます。

2つ目のShadowはselectを指定していないので、渡されたlight DOMで残っているもの全てがswipeされる用のコンテンツとしてinsertされます。

上から順番にselectで指定したセレクタにマッチするものはLight DOMから取り除かれ、次のcontentタグではその取り除かれて残ったDOMに対してselectが効いていく感じです。
理解すればなんてことないですが、この挙動には凄く違和感がありますねぇ。

重要な点は自作のタグと共にマークアップした内容(Light DOM)をWeb Componentsが受け取って、任意のinsertion pointを指定してShadow DOMとして表示できるということです。

また、今回はアクション用のbuttonもWeb Component “left-swipe-action-button“として定義しました。
left-swipe-actionと同様にleft-swipe-action-button.htmlにcomponentの実装を書きます(少しCSSを適用するだけのwapperコンポーネントです)。

left-swipe-action側ではleft-swipe-action-buttonを使用するため、依存関係を明記しておきます。

冒頭のpolymer-elementの宣言の上にlinkタグを用いて読み込みます。

<link rel="import" href="left-swipe-action-button.html">

よって、left-swipe-actionはleft-swipe-action-buttonを持つcompositeコンポーネントとなっています。
今回は使っていませんが、再利用性を高めるためにコンポーネントはextendsを使って継承の仕組も提供されています。

2.5 テスト

Polymerはまだまだ全てのブラウザで動く様な互換性はありません。現段階では、IE9やAndroidの標準ブラウザでは動きません。

Polymerのサポートブラウザ
http://www.polymer-project.org/resources/compatibility.html

今回はtest用のフレームワークは使わず、Chrome、Firefox、iOS safari、Android Chromeで軽く動作チェックを行いました。

しっかりとテストしたい場合のガイドは公式ドキュメントに記載されています。
http://www.polymer-project.org/resources/tooling-strategy.html#building-amp-testing


3. Github ioでデモを動かす

コードが出来上がって、ブラウザで動くことを確認したら、githubにpushして、github.ioに公開しましょう。

公開手順
https://pages.github.com/

gh-pagesという名前のブランチを作成するだけです。
プロジェクトの直下にdemo.htmlというdemo用のファイルを作りましょう。はっきりとわかっていないですが、component kitchinのようなgalleryサイトはプロジェクト直下のdemo.htmlをでもファイルとして自動判別している気がします。


4. Readmeをしっかり書く

あなたのWeb Componentsを使う人がまず使おうと思った時に見るのはReadmeだと思います。きちんと書かれていないと、メンテナンスもされてないと思われ使ってもらえないでしょう。

ということで、つたない英語でも最低限使い方くらいはちゃんと明記しておきます。


5. Bowerに登録

今やJSのライブラリを管理するのにBowerは定番ですよね。公式のPolymerのガイドでもbowerへの登録が推奨されています。
http://www.polymer-project.org/articles/distributing-components-with-bower.html

まずは自作コンポーネントが依存するものはbower.jsonのdependencyに記述しましょう。
今回はpolymerだけでなく、touchイベント等の互換を提供するpointerevents“>pointereventsも利用したので以下の様になっています。

bower.json

{
  "name": "left-swipe-action",
  "version": "0.1.0",
  "authors": [
    "tejitak"
  ],
  "description": "Left swipe action with Polymer web components",
  "keywords": [
    "Polymer",
    "web-components"
  ],
  "license": "MIT",
  "homepage": "https://github.com/tejitak/left-swipe-action",
  "dependencies": {
    "polymer": "Polymer/polymer#~0.3.4",
    "pointerevents-polyfill": "~0.2.0"
  },
  "ignore": [
    "**/.*"
  ]
}

実はこのkeywordsで”Polymer“と”web-components“を指定するところが重要です!このキーワードによりgalleryのサイトがWeb Componentsの登録を自動的に検知してくれます。

bowerにregisterする前に、localでテストして確認する方法があるので、テストはします。
http://blog.edouard-lopez.com/testing-bower-dot-json-locally-before-registering-package/

テスト環境で、bower install {your-component} —saveして、適当なhtmlファイル作ってPolymerを読み込み自作タグが動作したら、bowerにregisterしましょう!

タグしてpush。(タグはx.x.xの形式で)

git tag -a 0.1.0 -m "Tagging 0.1.0"
git push --tags

bowerに登録!

bower register left-swipe-action git://github.com/tejitak/left-swipe-action.git

登録できたらbower searchでちゃんと登録されていることを確認。

>bower search swipe-action
Search results:

    left-swipe-action git://github.com/tejitak/left-swipe-action.git

念のため、installして動作確認。

bower install left-swipe-action --save

6. Galleryサイトに登録されているか確認

どのくらいの頻度で更新されるのかは不明ですが、bower.jsonのキーワードに”Polymer”と”web-components”を指定したため、数時間後にはギャラリーのサイトに自動的に登録されていました。

Custom Elements A Web Components Gallery for Modern Web Apps
http://customelements.io/

Web Components Catalog – Component Kitchen
http://component.kitchen/

スクリーンショット 2014-08-09 13.28.48

上記の様にLive Demo付きで登録されていれば、自作コンポーネントの公開完了です!!


以上が、Componentを作り始めてから公開するまでの道のりの紹介でした。まだ、ブラウザのサポートだとかパフォーマンスの面でプロダクション用にWeb Componentsをばりばり使うというのは難しいかもしれません。ただ、Web ComponentsはJSのライブラリというレベルではなくブラウザnativeで実装される予定のテクノロジーであるため、今後どんどん普及して行くのは間違いないと思っています。

作るのはなかなか面白かったので、引き続き趣味でWeb Components作っていこうかと思います〜(仲間募集中)。