まず、Polymerとは何かということですが、詳細は、「2018年に注目したい3つのWebテクノロジー:Web Components、Progressive Web Apps(PWA)、Universal JavaScript」を読んでいただくとして、ざっくりというと、次のとおりです。
- Polymer: Web Componentsを利用したJavaScriptのフレームワーク
- Web Components: 新しいHTMLのタグをユーザーがつくるW3C標準の技術
昨年から注目していたPolymer。たまたま、部署の20%ルールで自由にアプリをつくれることになったので、Polymerを採用しました。
しかし、次を見ていただいてもわかるとおり、Polymerはそれほど広まっているとはいえず、困ることも色々とありました。
そこで、Polymerを使って、苦労したところをシェアします。
開発環境で苦労したところ
PolymerにはStarter Kitがあって、コマンドをたたけばひな形ができるので、webpackやgulpをゴリゴリ書いたりしなくてもすぐに使えます。
それはよかったのですが、困ったのは次の3つです。
- コードの整形
- コード解析(lint)
- ブラウザのオートリロード
コードの整形
htmlとcssとJavaScriptが一つのファイルに混ざっているので、今まで使っていたコード整形ツールがうまくいきません。
そこで、調べた結果、次の2つが候補になりましたが、今はPrettierを使うことにしました。
webmatはPolymerチームが作っているツールでPolymer Elementsはこれを使っているようです。そう考えるとこれがよさそうですが、使ってみると、逆に崩れてしまうところがあります。
clang formatなので、色々と設定を変えて試してみましたがうまくいかず、early developmentで情報も少ないので、断念しました。
一方、Prettierは、HTMLやCSSの部分が整形されないので微妙なのですが、JavaScriptの部分はうまく動作し、有名で広く使われているツールなので、こちらがベターだと判断しました。
コード解析(lint)
Polymerにはpolymer lintが用意されているのですが、polymer独自部分にフォーカスされていて、HTML, CSS, JavaScriptのベーシックな部分はあまりチェックできていない様子。
また、Polymer IDEというのがあって、Visual Studio CodeとAtomにプラグインとして導入できます。そこで走るlintがPolymer 3に対応しきれていないようで(Atom版のみ検証しました)、問題ないところでWarningを出します。PolymerのSlackを調べたところ、コメントを書いて対処しているようですが、それもなんだか違う気がします。
今のところ、Polymer IDEは使わず、polymer lintで定期的にチェックするのみで、HTML, CSS, JavaScript用に別のlintを入れることはしていません。
ブラウザのオートリロード
Polymerはpolymer serveというコマンドでローカルサーバーを起動するのですが、コードを変更してもオートリロードしません。
starter kitで一通りそろっているのに、これは変だなぁと思い、調べてみましたが決定版はなさそうでした。
結局、有名なものを使おうということで、Browsersyncを入れて、npm scriptを書いてnpm startで動かすようにしました。
"scripts": {
"start": "polymer serve --port 8081 | npm run watch",
"build": "polymer build",
"lint": "polymer lint",
"test": "polymer test",
"test:integration": "polymer build # test that psk builds without error with the CLI",
"watch": "browser-sync start --browser 'Google Chrome' --proxy localhost:8081 --files \"src/**/*.*, index.html, *.js\""
}
コードで苦労したところ
どんな技術でも、公式サイトの情報を読むのが基本ですが、ドキュメントを頭から全部読み込んでから使い始める人は稀だと思います。
Get Startedをやって、ざっと眺めたら手を動かして、必要に応じて検索するのではないでしょうか。
Polymerはそれがやりにくいのが最大の問題です。検索すると、Polymer1, 2の古い情報がでてきて、なかなか該当部分にたどりつかない。これは、地味なようで結構ストレスになります。
以下に、記憶に残っている範囲で困ったところを書いてみます。
データバインディングの通知
Polymerではデータバインディングしたデータは普通に変更しても検知されません。以下のように、this.set()
、this.push()
で変更するか、notifyPath()
で変更を通知してあげる必要があります。
// セット
this.set('プロパティ名', 値);
// 追加
this.push('プロパティ名', 値);
// 通知
this.notifyPath('プロパティのパス');
ここで、注意すべきはObjectと配列の下位のデータを変更するときです。次のような操作をしても、データの変更が通知されません。
// unobservable subproperty change
this.address.street = 'Elm Street';
// unobservable change using native Array.push
this.users.push({ name: 'Maturin'});
次のようにする必要があります。
参照:Work with object and array data
// Notify Polymer that the value has changed
this.notifyPath('address.street');
// mutate an object observably
this.set('address.street', 'Half Moon Street');
// mutate an array observably
this.push('users', { name: 'Maturin'});
// mutate an object observably
this.set('address.street', 'Half Moon Street');
// mutate an array observably
this.push('users', { name: 'Maturin'});
ところが、これは結構大変で、配列やObjectを丸ごと書き換える場合などに困ります。ReactやVueに比べると、このあたりが使いにくいと思います。
プロパティ名
細かいところですが、すごくハマったのがプロパティ名のルールです。JavaScriptなので、変数名はCamel(xxxYyyZzzのような)にしていることが多いかと思います。その場合、プロパティ名もCamelにしたくなるかと思いますが、そうするとうまく動きません。例えば、次のようなコードはうまく動きません。
<custom-element yourPropertyName={{yourPropertyName}}></custom-element>
次のようにプロパティ名はケバブ(xxx-yyy-zzz)にする必要があります。
<custom-element your-property-name={{yourPropertyName}}></custom-element>
ちゃんとドキュメントを読むと、次のとおり、きちんと書いてありますが、気づかずにかなりの時間を浪費してしまいました。
ただ、よくよく考えてみると、HTMLの世界では大文字と小文字が区別されないので、あるべき形というか、しかなたないのかなぁとも思いますが。。。
Use data binding and properties
Learn more: attribute and property names. You'll note that the markup above uses toggle-icon, not toggleIcon. Polymer represents camelCase property names using dash-case attribute names. To learn more, see Property name to attribute name mapping in the Polymer library docs.
HTMLのプロパティ
これも細かいのですが、HTMLのclassなどのプロパティを使うときは次のように$=
とする必要があります。これもドキュメントにきちんと書かれていますが、data属性を使ったときにうまく動かなくて、原因がわからずに苦労しました。
Native properties that don't support property binding
<!-- class -->
<div class$="[[foo]]"></div>
<!-- style -->
<div style$="[[background]]"></div>
<!-- href -->
<a href$="[[url]]">
<!-- label for -->
<label for$="[[bar]]"></label>
<!-- dataset -->
<div data-bar$="[[baz]]"></div>
<!-- ARIA -->
<button aria-label$="[[buttonLabel]]"></button>
おまけ(このアプリで使ったもの)
Firebase
サーバーサイドは広義の意味でサーバーレスで、mBaaSとしてFirebaseのHosting、Realtime DatabaseとAuthenticationを使っています。
ちなみに、次のとおり、狭義のサーバーレスのCloud Functionsも使っています。
GCP Cloud Functions
Google Natural Language APIを使うために、サーバー側での処理が必要になったのですが、Firebaseのホスティングは静的ファイルしかおけない。
いろいろ考えた結果、クライアントからリクエストをうけて、APIを呼んで結果をクライアントに返すだけなので、サーバーレスがベストだろうということで選択しました。
Google Natural Language API
形態素解析を利用。Sentiment(感情解析)も利用予定。
Google AutoML(予定)
チャットの文言から、「将来やりたいこと」や「今課題に思っていること」などを分析するためにAutoMLを使う予定です。
一つの分類につき100程度のデータでOKで、2つ以上の分類があれば利用できるという優れもの。
今はまだ「将来やりたいこと」のデータが70位しかないので、もう少しデータを集めたら利用します。