Technote

by sizuhiko

Toilet EvolutionのフロントエンドをPolymer3対応する(4)

この記事はToilet EvolutionのフロントエンドをPolymer3対応する(3)の続編です。

前回までの対応で、ページをロードしたときに、ブラウザのコンソールにエラーが出ることが少なくなりました。 ただし多くのページで表示が崩れていたり、うまく表示できないエレメントがあります。 これは今回の ES Modules に対応してなかったエレメントに関連するところだったのですが、これらの対応を解説していきます。

dom-module has style outside template というワーニングが出る

ブラウザのコンソールを確認していると dom-module has style outside template といったワーニングが表示されます。 これは <dom-module> の中で <template><style> が並列に記述されている場合に発生します。

なので、すべて <template> の子要素に <style> を移動しました(コミット)。

動かすようにするための微調整

続いて1つずつの修正はそれぞれ大きくないものの、動作するまでの調整をたくさん実施しました(コミット)。

クリック処理が動かない

これは1から2の段階で廃止になった listeners を使っていたためで、利用箇所を addEventListener や、 on-tap, on-click に変更しました。

moment でロケールを利用しようとするとエラーになる

Toilet Evolution では、トイレの利用状況を表示するときに 10分前 のような表示をしているのですが、これに moment.js の機能(toDate())を使っています。 moment だけ(ロケールを使わない)なら npm でインストールして

import moment from 'moment';

と記述すればよかった(動いた)のですが、どうしてもロケールの解決がうまくいかない(Webpackするとエラーになる)ので、今回はあきらめてHTML側でグローバルに利用できる形にしました。

  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/locale/ja.js"></script>

いくつかWebpackとmomentのissueも見つけて解決策もありそうだったのですが、とりあえず困らないのでこの方法で。

style や slot が解決されない

modulizerで自動変換された <google-map-marker> エレメントは以下のようになっていました。

const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = `<dom-module id="google-map-marker">
  <style>
    :host {
      display: none;
    }
  </style>
  <template><slot></slot></template>
</dom-module>`;

document.head.appendChild($_documentContainer.content);

もとのHTMLでの記述は以下のようになっていました。

<dom-module id="google-map-marker">
  <template>
    <style>
      :host {
        display: none;
      }
    </style>

    <slot></slot>
  </template>
  <script>

自分が作ったカスタムエレメントでは、 Polymer_templatehtml を使うように変換されていたのですが、bowerでインストールしたコンポーネントの多くは上記のように変換されていました。これを html を使うようにしてみたところ解決しました。

Polymer({

  _template: html`
  <style>
    :host {
      display: none;
    }
  </style>
  <slot></slot>
`,

<google-map> エレメントも同様にうまく動作しなかったので、 html をつけて _template で返却するように対応しました。

さいごに

4回にわたって、Polymer1のプロジェクトをPolymer3に移行した解説をしてきました。 この連載がPolymer1からアップグレードする他の方の手助けになれば幸いです。

基本的な部分は、ほとんどmodulizerで変換できるので、これは便利だなーと思うのでした。 Polymer3対応した依存エレメントは、どこかのタイミングでPRを出してフィードバックしたいなと思っています。

では良いWeb Componentsライフを!

Toilet EvolutionのフロントエンドをPolymer3対応する(3)

この記事はToilet EvolutionのフロントエンドをPolymer3対応する(2)の続編です。

今回もmozlier適用だけでは動かないので、ローカルで動作するように修正というコミットにおける試行錯誤を書いていきます。

前回は多くのアプリケーションや、カスタムエレメントで実施しなければならない共通的な修正箇所について解説しましたが、ここからはアプリケーションで使っている機能やエレメントについて、修正が必要だった箇所について解説していきます。

ES Module対応になっていないエレメントを使う

前回の記事で「bower_components を使わないようにする」について解説しました。 PolymerチームやVaadinチームなどが作っているコンポーネントについては、ほとんど ES Module に対応しているのですが、場合によっては対応してないモジュールを使っていることがあるかもしれません。

Toilet Evolutionでは以下のエレメントが ES Module に対応していませんでした。iron-signalstoast-er の依存に関連しています。

そこで、これらのエレメントは modulizer で変換されたものを、そのまま利用することにしました。 また変換されたエレメントについては、前回の記事同様に共通的な修正箇所が必要になります。 つまり自分で作ったエレメントと同様に、そのエレメントが依存している(bower.jsonに含まれている)エレメントやライブラリについても、 npm install で取得するように変更する必要があります。

またPolymer1用のライブラリだったりする場合は、Polymer1から2へのアップグレードガイドに書いてある slot の対応なども必要だったので、そのライブラリが最新版だとPolymerのどのバージョンに対応したものなのかを確認しておくことが重要です。

そして、これらの作業が結構やっかいです(自分で作ったものでないので、ソースを理解する必要があります)。

以前のPolymerのビヘイビアは個別にインストールする必要がある

HTMLインポートで利用していた、Polymerチームが作っていたようなカスタムエレメントのビヘイビアは、個別に import するか、場合によっては npm install で導入する必要があります。 自分で作ったエレメントでは、ビヘイビアを使っていなかったので、この段階で初めて知りました(これは意外にもドキュメントに書いていません)。

例えば <gold-password-input> では、PaperInputAddonBehavior という paper-input に含まれていたビヘイビアを使っています。 これは以下のように、import を追加してあげる必要があります。

import {PaperInputAddonBehavior} from '@polymer/paper-input/paper-input-addon-behavior.js';

ES Module対応しているエレメントであれば、webcomponents.orgのビヘイビアを参照すると解説が書いてあります。 上記の PaperInputAddonBehavior であれば、こちらです。

Polymer.dom を変更する

これも自分のエレメントでは使っていなかったので気付かなかったのですが、使っているエレメントがあったので修正の必要がありました。 まず dom をインポートします。

import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'

で、Polymer.dom を使っている箇所を dom に変更します。例えば以下のような感じです。

return dom(this).parentNode;

bowerでカスタムエレメント以外のライブラリに依存している

<gold-password-input>zxcvbnというパスワードストレングスエミュレータを使っています。 Node.jsなどにも対応しているのですが、今回はbowerでインストールしたフォルダをそのままコピーして、リポジトリに追加しました。 このようにカスタムエレメント以外のライブラリを使っている場合は、npmでインストールしてビルドする方法以外の検討も必要となります。 特に<gold-password-input>では、以下のように script タグを動的に追加してライブラリをインポートしていたため、npmを利用するのに適していなかったという理由があります。

  ready: function() {
    isZxcvbnLoaded = typeof zxcvbn !== "undefined";
    if (!isZxcvbnLoaded) {
      isZxcvbnLoaded = true;
      var oScript = document.createElement("script");
      oScript.type = "text\/javascript";
      oScript.onerror = function(err) {
        isZxcvbnLoaded = false;
        throw new URIError("The script " + err.target.src + " is not accessible.");
      };
      this.parentNode.insertBefore(oScript, this);
      oScript.src = this.resolveUrl("../zxcvbn/dist/zxcvbn.js");
    }
  },

このぐらいまで対応が進むと、ページをロードしたときに、ブラウザのコンソールにエラーが出ることが少なくなります。 ただし多くのページで表示が崩れていたり、うまく表示できないエレメントがあります。 これは今回の ES Modules に対応してなかったエレメントに関連するところなのですが、 これらの対応方法については次回解説していきます。

Toilet EvolutionのフロントエンドをPolymer3対応する(2)

この記事はToilet EvolutionのフロントエンドをPolymer3対応する(1)の続編です。

今回からはmozlier適用だけでは動かないので、ローカルで動作するように修正というコミットにおける試行錯誤を書いていきます。

うまく動作させるために試行錯誤して変更したファイル、実に110。1回では書ききれない予感が…

不要になったファイルを削除

bower.json.bowerrc ファイルは、Polymer3でnpmに移行したので、削除します。

linterを追加

lintは polymer-cli にも付いているのですが、エディタで編集中にも状況は知りたいので、eslintを導入します。 Polymer1のころはHTMLファイルだったので、特に不要でしたね。

ビルドを変更

これまでbuildは独自にgulpで実装していたのですが、Polymer3対応をするに伴いpolymer-buildを使うようにしました。 これは polymer CLI でも使っているビルドライブラリです。ビルドについては、この連載で触れていきます。

polymer.jsonの変更

shellfragments で指定したファイルの拡張子を html から js に変更します。 これは自動的に変換されないので注意しましょう。 extraDependencies や、そのほかの定義も変わっていますが、このあたりはpolymer.json specificationを参考にPolymer3形式に1から記述するぐらいの気持ちでいましょう。

変換された自作のカスタムエレメントを修正する

まずmodulizerで変換すると、Polymer本体のimportが

import '../bower_components/polymer/polymer-legacy.js';

のようになっているので、これを

import '@polymer/polymer/polymer-legacy.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';

のように変更しました。

なお、公式ドキュメントによると

import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';

にしていますが、これは PolymerElement 使っている場合なので、ハイブリッドモードの書き方の場合は、私のようにlegacyを使ってください。

続いてテンプレートが以下のように Polymer.html を参照するようになっています。

Polymer({
  _template: Polymer.html`

この記述は利用できないので、以下のように import した html を利用するように変更します。

Polymer({
  _template: html`

bower_components を使わないようにする

変換されたjsファイルは、bower_components上に変換されたモジュールを参照しにいきます。 そこで各々のパッケージがnpmに公開されているか調べて、npmに移行していきます。

すでに上記で bower_components/polymer を変更していますが、これを例に説明します。

  1. まずwebcomponents.orgのサイトでWebComponentsを検索します。
  2. この例では polymer を入力して検索すると、該当のものが見つかるので、詳細ページを開きます。
  3. もし ES Modiles に対応していれば、左側に View on NPM が表示されます。
  4. INSTALLED VIA NPM のリンクをクリックすると npm install --save Polymer/polymer のように表示されるので、CLIからインストールします。
  5. bower_componentsから、参照しなくなったjsファイルを削除します。

WebComponents V1対応に追従する

自分が記述したCustomElementsには利用していなかった <content> も、依存関係のタグでは利用している場合があります。 例えば@polymer/app-layoutは、レイアウトないのコンポーネントを識別するときに利用しているので、slot対応を行います。たとえば以下のドロワー記述は

      <app-drawer>

次のように slot 識別を追加します。

      <app-drawer  slot="drawer">

このあたりの修正ポイントは、タグが表示されなくなることで気がつきますが、アップデートしたら最新のドキュメントを一読することをオススメします。 他にも修正箇所があるかもしれませんので。

importHref を修正する

iron-pages でページを切り替えるとき、Polymer1では動的に HTML import を実行していました。

  _pageChanged: function(page) {
    this.importHref('/elements/te-' + page + '.html', null, null, true);

ES Moduleを使うPolymer3では以下のように書き換えます。

    switch (page) {
      case 'admin':
        import('./te-admin.js');
        break;
      case 'devices':
        import('./te-devices.js');
        break;
      case 'login':
        import('./te-login.js');
        break;
      case 'about':
        import('./te-about.js');
        break;
    }

import の引数には変数が利用できないので注意してください。

Polyfillを変更する。

HTMLファイルではpolyfillLをインポートしています。 Polymer1では /bower_components/webcomponentsjs/webcomponents-lite.min.js を使いましたが、Polymer3では以下のpolyfillに変更します。 これもnpmを使ってインストールするように変更します。

<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>

このように修正を重ねていくことで、徐々に動作するエレメントが増えてきます。 ここまでは多くのアプリケーションや、カスタムエレメントで実施しなければならない共通的な修正箇所です。 Toilet Evolutionでは、まだまだ修正箇所がありましたので、続きは次回で。

Toilet EvolutionのフロントエンドをPolymer3対応する(1)

私が開発運用しているToilet Evolutionは、フロントエンドをPolymer(v1)で構築していました。 これは古いWebComponentsの仕組みを使っているので、常々アップデートしないとなーと思いながらも、長いこと重い腰があがりませんでした。

しかし以下のツイートを見て「よしやるぞ」という気持ちにスイッチが入り、移行を実施しました。

実際Toilet Evolutionのサイトはもう最新に置き換わっております。 その様子はToilet EvolutionのGitHubリポジトリのコミットログを追うことでもわかったりしますが、かなり頑張ったので、その過程を連載でブログにまとめようと思いました。

この記事はその1回目です。

Polymer1プロジェクトをPolymer3へ移行するには

マイグレーションガイドは1から2、2から3に向けてあります。 1から3のガイドがあるわけではないので、両方を事前に読んでおくと良いです。

Polymer2は1の記法が使えるハイブリッドモードがあるのと、そもそも1から2で変更が必要な記述を使っていないので、実際はmodulizer一発でいけるんでは?と考えました。 なので、1から2の手順は気にせず、進むことにしました。

何はともあれアップデート

npmのライブラリもだいぶ古くなっていたので、まずは最新に更新しました。 Gulpを3から4にあげたので gulpfile.js もモリモリと変えていきます。

続いて、bowerに入っているパッケージも新しくしていきます。これは modulizerのガイドに書いてあるので、そのまま実施します。

続いてmodulizerを実行しますが、commitログではそこまでの手順で迷走しています。あまり気にしないでくださいw

modulizerで変換する

modulizer --out . を実行します。すると html で記述していたカスタムエレメントが js ファイルに変更されます。 ここまでのコミット

変換するとだいたい良い感じになっています。 でも変換だけでは動きません。ここで以下のような箇所が変換されないことがわかります。

これらの手順は次回へ続きます。

esm LTでブラウザの最新動向について話しました

esm LTとは永和システムマネジメントの社内LT大会のようなもので、福井にゆかりのあるやきとりの名門 秋吉のやきとりとビールを片手にワイワイ楽しむイベントです。

自分で写真撮るの忘れたので、同僚のツイートを引用。

私が話したのは「ブラウザの最新動向について」ということで、PHPerKaigiでも話した import maps と、Chrome73から入ったDevToolsの Add Logpoints について話しました。

ChromeのDevToolsに入った新しい機能とか、そもそもWeb Updatesみんな読んでないよね、ってことで、新しい機能便利なんでみんな読もう!ってことです。

Logpointsに関してはWhat’s New In DevTools (Chrome 73)の記事を読んでください。 最初に紹介されます。 これからみんなソースコードにデバッグ用途の console.log() を書くの禁止な!