Polymerでウェブサイト作ってみた話

この前の技術書典３でWeb Componentsの本を頒布したんですが、その時にサークルのウェブサイトをPolymerを使って作ったのでその感想をまとめておこうかなと思います。
https://inutetraplus.netlify.com/
(画像が重いのは目をつぶってください…)

ちなみに、電子版をBOOTHでも販売してます。
[イヌでもわかるWeb Components](https://inutetraplus.booth.pm/items/664304)

## 作り方
今回は`polymer-starter-kit`という公式テンプレートを使いました。
Polymer CLIが必要なのでインストールしましょう。

```
npm install -g polymer-cli
```

あと、Polymer２はまだbowerを使っているので、入ってない人はこちらもインストールしましょう。

```
npm install -g bower
```

プロジェクトフォルダを作成し、テンプレートを生成しましょう。

```
mkdir my-app
cd my-app
polymer init polymer-2-starter-kit
```

### polymer-starter-kitの中身
中身はこんな感じになってます。

```
├── README.md
├── bower.json
├── images
├── index.html
├── manifest.json
├── package.json
├── polymer.json
├── service-worker.js
├── src
│   ├── my-app.html
│   ├── my-icons.html
│   ├── my-view1.html
│   ├── my-view2.html
│   ├── my-view3.html
│   ├── my-view404.html
│   └── shared-styles.html
├── sw-precache-config.js
└── test
    ├── index.html
    └── my-view1.html
```

Polymerで必要なものがだいたい一通り揃ってるとても優秀なスターターキット。
ルーティングやServiceWorkerの設定ファイルとかも生成されます。

`src`配下の`my-app.html`がルートコンポーネントのファイルで、ルーティングや共通パーツ（ヘッダー、フッターなど）の読み込みなどを行います。
その他の`my-viewX.html`が各ページのコンポーネントです。HTML/CSS/JSをこれらのファイルの中に書きます。

### ルーティングの設定
ルーティングは`my-app.html`に記述します。
スターターキットでは`app-route`と`iron-pages`というコンポーネントを使ってルーティングをしています。

```html
<app-location
    route="{{route}}"
    url-space-regex="^[[rootPath]]">
</app-location>

<app-route
    route="{{route}}"
    pattern="[[rootPath]]:page"
    data="{{routeData}}"
    tail="{{subroute}}">
</app-route>
```

```html
<iron-pages
    selected="[[page]]"
    attr-for-selected="name"
    fallback-selection="view404"
    role="main">
  <my-view1 name="view1"></my-view1>
  <my-view2 name="view2"></my-view2>
  <my-view3 name="view3"></my-view3>
  <my-view404 name="view404"></my-view404>
</iron-pages>
```

```js

static get properties() {
  return {
    page: {
      type: String,
      reflectToAttribute: true,
      observer: '_pageChanged',
    },
    routeData: Object,
    subroute: String,
    // This shouldn't be neccessary, but the Analyzer isn't picking up
    // Polymer.Element#rootPath
    rootPath: String,
  };
}

static get observers() {
  return [
    '_routePageChanged(routeData.page)',
  ];
}

_routePageChanged(page) {
  // If no page was found in the route data, page will be an empty string.
  // Default to 'view1' in that case.
  this.page = page || 'view1';

  // Close a non-persistent drawer when the page & route are changed.
  if (!this.$.drawer.persistent) {
    this.$.drawer.close();
  }
}

_pageChanged(page) {
  // Load page import on demand. Show 404 page if fails
  var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');
  Polymer.importHref(
      resolvedPageUrl,
      null,
      this._showPage404.bind(this),
      true);
}

_showPage404() {
  this.page = 'view404';
}
```

ページコンポーネントの命名規則を変えたいときは`var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');`の部分を変更します。

## Header、Drawer
スターターキットでは`app-layout`というコンポーネントを使ってレイアウトを構築しています。
これはPolymer公式のレイアウトコンポーネントで、マテリアルデザインっぽい感じになります。
これを使わないといけないというわけではないですが、今回のウェブサイト制作では、このレイアウトのまま作成しました。

```html
<app-drawer-layout fullbleed narrow="{{narrow}}">
  <!-- Drawer content -->
  <app-drawer id="drawer" slot="drawer" swipe-open="[[narrow]]">
    <app-toolbar>Menu</app-toolbar>
    <iron-selector selected="[[page]]" attr-for-selected="name" class="drawer-list" role="navigation">
      <a name="view1" href="[[rootPath]]view1">View One</a>
      <a name="view2" href="[[rootPath]]view2">View Two</a>
      <a name="view3" href="[[rootPath]]view3">View Three</a>
    </iron-selector>
  </app-drawer>

  <!-- Main content -->
  <app-header-layout has-scrolling-region>

    <app-header slot="header" condenses reveals effects="waterfall">
      <app-toolbar>
        <paper-icon-button icon="my-icons:menu" drawer-toggle></paper-icon-button>
        <div main-title>My App</div>
      </app-toolbar>
    </app-header>

    <iron-pages
        selected="[[page]]"
        attr-for-selected="name"
        fallback-selection="view404"
        role="main">
      <my-view1 name="view1"></my-view1>
      <my-view2 name="view2"></my-view2>
      <my-view3 name="view3"></my-view3>
      <my-view404 name="view404"></my-view404>
    </iron-pages>
  </app-header-layout>
</app-drawer-layout>
```

## 使ってみた感想

* CLIがあって環境設定とか特になくても使えるのが嬉しい
* 学習コストは低いと思う（他のライブラリに比べれば）
* HTML/CSSにスコープが設定されて幸せ
* CSSとHTMLが近いので見通しが良い
* CSSで変数が使えるの嬉しい
* CSSをどこまで共通にするか悩んだ
* ルーティングまわりのイベントハンドリングがややこしい
* ServiceWorkerの設定とかもやりやすくて良い
* カルーセルなどのコンポーネントは少ないし挙動が怪しかったりするので、自分で実装したほうが良さそう
    * 今後に期待