LoginSignup
34
32

More than 5 years have passed since last update.

PolymerをES6で書く

Last updated at Posted at 2015-08-25

@laco0416です。Polymerが案外カジュアルにES6で書けるとの話を聞いたので実際に書いてみました。

追記(2015/08/27)

↓の記事を書いた後にES6との親和性を改善する変更が入ったので次のように書けるようになりました。 beforeRegisterイベントハンドラがdesugar処理の前に処理されるようになったのでここでispropertiesなど、今までオブジェクトのプロパティとして定義していたものを初期化できます。また、Polymer(MyElement)のようにclass(実体はFunction)をプロトタイプとして渡すことが可能になりました。

class MyElement {

  beforeRegister() {
    this.is = "my-element";
    this.properties = {
      foo: String
    };
  }
}

Polymer(MyElement);

だいぶ自然に書けるようになりましたね。まだリリースされていないのでbowerからのインストールでは利用できませんが、この変更はPolymer SummitのES6関連のセッションで取り上げられるようです。

結論

十分実用的なのですでにBrowserify+Babelする環境あるなら余裕。なくてもきっかけになるかも。

事の発端

日頃からPolymerのAPIはイケてないなと思っていて、大体の原因はES5にあると決めつけているので時々「PolymerはES6対応したら化ける」とぼやいているのだけど、今日もPolymerのSlackでES6 classとか使って書けるようにならないの?って投げていたらすでにChromeチームが実験的にやってることが判明した。

サンプルとはいえES6 + Browserify + Babel + Web Audio API + Service Workerと詰め込みすぎやろ!と突っ込みたくなるアプリケーションである。

このrepoのgulpfileを参考に自分でES6とPolymerの親和性を確かめてみた。

準備

構成

├── bower.json
├── bower_components
├── dtsm.json
├── gulpfile.ts
├── node_modules
├── package.json
└── src
    ├── elements
    │   └── x-hoge
    │       ├── x-hoge.html
    │       └── x-hoge.js
    ├── index.html
    └── scripts
       └── lib.js

bower.json

...
  "dependencies": {
    "polymer": "~1.1.1"
  }
...

package.json

...
  "devDependencies": {
    "babelify": "^6.2.0",
    "browserify": "^11.0.1",
    "del": "^1.2.1",
    "gulp": "^3.9.0",
    "gulp-vulcanize": "^6.0.1",
    "run-sequence": "^1.1.2",
    "typescript": "^1.5.3",
    "typescript-require": "^0.2.9",
    "vinyl-source-stream": "^1.1.0"
  },
...

dtsm.json

...
  "dependencies": {
    "gulp/gulp.d.ts": {
      "ref": "a1226afe5c4ab96610e088d970eb4f8537b1d116"
    }
  }
...

やる

x-hoge.html

今回は <x-hoge> という要素を作る。

x-hoge.html
<link rel="import" href="../../../bower_components/polymer/polymer.html"/>
<dom-module id="x-hoge">
  <template>
    <h4>x-hoge</h4>
  </template>
</dom-module>
<script src="x-hoge.js"></script>

参照するx-hoge.jsはES6で書く。

x-hoge.js
import {Greet} from "../../scripts/lib"

class XHoge {

  get is() {
    return "x-hoge"
  }

  constructor() {
  }

  attached() {
    Greet("x-hoge");
  }
}
Polymer(XHoge.prototype);

importされるlib.jsは簡単なもの

lib.js
export function Greet(name) {
  console.log(`Hello ${name}!`);
}

x-hogeを呼び出すindex.htmlはシンプル

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <link rel="import" href="elements/x-hoge/x-hoge.html"/>
</head>
<body>
  <x-hoge></x-hoge>
</body>
</html>

gulpfile

もちろんこのままではES6が解釈できずにブラウザでは動作しないので、Babelを通さないといけない。今回はBrowserifyとBabelifyで一挙に解決する。また、gulpfileをTypeScriptで書くのに最近便利さを感じているので今回もgulpfile.tsを書いた。

gulpfile.ts
///<reference path="typings/bundle.d.ts"/>

var gulp:gulp.Gulp = require("gulp"),
    del = require('del'),
    browserify = require("browserify"),
    babelify = require("babelify"),
    source = require('vinyl-source-stream'),
    runSeq = require("run-sequence");

const bundles = [
  {
    src: "./src/elements/x-hoge/x-hoge.js",
    name: "x-hoge.js",
    dist: "./dist/elements/x-hoge"
  }
];

gulp.task('clean', (done) => {
  del(['dist'], done);
});

gulp.task('root', ["clean"], () => {
  return gulp.src('./src/**/*.*')
      .pipe(gulp.dest('./dist/'));
});

gulp.task("build", ["root"], ()=> {
  bundles.forEach((bundle)=> {
    browserify({
      entries: [bundle.src],
      debug: true
    }).transform(babelify)
        .bundle()
        .on("error", (err) => {
          console.log("Error : " + err.message);
        })
        .pipe(source(bundle.name))
        .pipe(gulp.dest(bundle.dist));
  });
});

gulp.task("default", ["build"], () => {
});

流れとしては

  1. distを空っぽにする
  2. srcの中身をdistにコピーする
  3. bundlesに指定したjsをエントリポイントにBrowserify+Babelifyする

これでx-hoge.jsにlib.jsがconcatされ、さらにES5にトランスパイルされるのでブラウザでちゃんと動くようになる。

kobito.1440517953.868081.png

vulcanize

PolymerはHTML ImportsでXHRが増えるのでProductionではVulcanizeしてHTMLを結合してまとめようねというのが主流。オプションでJSやCSSもインラインに解決してくれるのでこの流れに含めてindex.htmlだけで全部解決できるようにする。

gulp-vulcanizeというプラグインがあるので使う。

gulpfile.ts
///<reference path="typings/bundle.d.ts"/>

var gulp:gulp.Gulp = require("gulp"),
    ...
    vulcanize = require("gulp-vulcanize");

...

gulp.task('vulcanize',() => {
  return gulp.src('./dist/**/*.html')
      .pipe(vulcanize({
        inlineScripts: true,
        inlineCss: false,
        stripExcludes: false
      }))
      .pipe(gulp.dest('./dist'));
});

gulp.task("default", ["build"], () => {
  setTimeout(()=> {
    runSeq(["vulcanize"]);
  }, 1000);
});

defaultタスクのなかでsetTimeoutするのはすんごい雑なHackで、本当はなくてもできるはずなんだけど、ウェイト無しでやると何故かトランスパイル前のES6のJSをインラインに展開してしまうということが発生した。Gulp弱者なのでこういうときどうするのが正しいのかわからない。

Vulcanize前はこんな感じ

kobito.1440518537.296586.png

Vulcanize後 当然ながらindex.htmlだけになる

kobito.1440518614.267125.png

まとめ

Gulpで躓いたくらいで他はすんなりいけたので思った以上に親和性高いという印象。
どんどんES6使うのが当たり前みたいな風潮作っていきたい。

余談

この「PolymerとES6一緒に使えるぜ」ネタ、Polymerチームが9月のPolymer Summitに向けて温めていたらしく最近コードが表に出てきたっぽい。Summit用のIssueは https://github.com/Polymer/polymer/labels/summit で見られる。まだES6系のしか上がってないけど今後増えるんじゃないか?と予想。

34
32
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
32