Technote

by sizuhiko

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() を書くの禁止な!

PHPer Kaigi 2019に参加しました

初めてのPHPerKaigi参加です。 これまで予定が重なっていて参加できなかったのですが、今年は年度末開催ということで、他の予定もなく参加することができました。

どうせなら何か話さないと、ということで

他はセッションを聞いていました。

CI/CDとは…

CI/CD相談会で受けた印象は、CI == 自動テストやってます、CD == ツール使ってデプロイしてます(完全手動ではない)という雰囲気の人が多く、ほんとうのCI/CDにはまだまだ遠いなぁという感じ。それでも私がテストやろうね、といってほとんどの人が書いてない時代からは大きな変化で、いつかほんとうの意味に近づいてくれることを期待しています。

import maps と HTML Template Instantiation について

アンカンファレンスではWeb Componentsの周辺仕様の動きについて、話しました。

このうちCSS Shadow PartsはCSSWGのドラフトになっていることもあり、概要をさらっと説明。 Custom Element RegisterHTML Modulesは、Web Components色が強いので、これも概要をさらっと。 で、import mapsHTML Template Instantiationについて、少し深く話しました。

import maps

これは過去のブログ記事でも登場した Package name maps の最新の名前です。過去のリンクからは自動的にリダイレクトされます。 bower によるJavaScript依存ライブラリのインストールが、npm中心のエコシステムに変わり、ブラウザからそのモジュールを利用するには Webpack のようなもので依存解決をする必要がありました。 しかし今日では ESModule Import がブラウザでもサポートされているので、以下のようなコードでライブラリを利用したくなります。

import moment from "moment";
import { partition } from "lodash";

しかしブラウザは from から読み取るときURIとして識別するので、当然サーバーからJavaScriptファイルを取れないし、Node.jsと違って package.json から main のスクリプトファイルをロードすることもできません。 そこで import maps では以下のようなマッピング定義を使います。

{
  "imports": {
    "moment": "/node_modules/moment/src/moment.js",
    "lodash": "/node_modules/lodash-es/lodash.js"
  }
}

するとブラウザはマッピングファイルから以下のように記述されたものとして解釈します。

import moment from "/node_modules/moment/src/moment.js";
import { partition } from "/node_modules/lodash-es/lodash.js";

これはWeb Componentsに関係なく、普通にHTMLからjQueryを使おうとしていた人にとってもわかりやすくなるし、Webpackからも解放されるので、はやくブラウザ実装になってほしいなぁと思っています。

HTML Template Instantiation

「テンプレートエンジンをブラウザ自体に」というのがこの仕様なわけですが、様々なフレームワークや handlebars のようなライブラリまで、テンプレートエンジンは数多くあります。ブラウザでやることは車輪の再開発なのか?という点については、RawレベルのAPIが利用可能になることでDOMの書き換えをブラウザがやってくれることはスピードアップにつながるし、ぜひとも進んでほしい仕様の一つです。 PolymerチームのJustinなど、各ブラウザベンダーの人も議論に参加しているので、期待できます。

フロントエンド相談会

Reactを使う人、Vueを使う人、Web Componetsを推進したい人(私)など、様々なお悩み、SSRの話しなども出ました。 技術ワードに踊らされるのではなく、それぞれの課題に対して議論できたのがとても良かったです。 IRT楽しかったですね。

出張版PHP勉強会@東京で話しました。

「PHPを学ぶということ」というタイトルで初心者セッションをやりました。

最近PHP勉強会で初心者の人多いなーと感じることが多いのと、開発チームでもプログラミングこれから!みたいな人にどうしたら良いのかなーと思うことがあって、まずは勉強の入り口にどうしたら良いかというキッカケを作ることができれば良いと思いました。

PHPコミュニティのSlackへの参加者が、このセッションをきっかけに結構増えたので、良かった!

さいごに

PHPerKaigi とても楽しかった。 交流がメインのカンファレンスらしく、多くの人と、様々な話題について議論できました。 さぁ、次は福岡に行きますよ!