kitak blog

Kみたいなエンジニアになりたいブログ

ルパート・サンダース監督 Ghost in the Shell を観た

今日の午前中に、渋谷の TOHO シネマズで観た。

都市や街並みの映像が良かった。単純にきれいだし、アニメ映画版、イノセンスと続く、ブレードランナーの DNA の系譜を継いでいるように感じた。 また、ひとつひとつのシーンの撮り方や、各所に散りばめられた小道具から、アニメ映画版やイノセンスに対するリスペクトも感じた。ガブリエルかわいいよ。

一方、映画の時間の長さ上、致し方ない気がするのだけど、公安9課のポジションがいまいちパッとしなかったのと、ひとつひとつの場面が明快で、全体としてきれいにまとまりすぎていて物足りなさを感じてしまった。イノセンスのように難解でなくていいのだけど、Stand Alone Complex の Stand Alone エピソードのような見終わった後の余韻、Complex エピソードのようなもう一度見たくなる程度の難しさが脚本に欲しかった。映像の質やスカーレット・ヨハンソンが演じる少佐の演技が良かっただけにそこがもったいないな、という気持ちになってしまった。

Vue.js v2 で揮発性の現象を扱う(ダイアログ等を実装する)

同僚に題の相談を受けていたんですが、そのときに「あんまりこの話、ググっても見かけないね」という話になったのでブログに書いておきます。

SPA でダイアログを実装する機会があると思うんですが、よくある事前にコンポーネントツリーのルート直下にダイアログのコンポーネントを入れておく実装だと、マークアップスタイルシートの事情でその位置に入れるのが難しかったり、テンプレートにダイアログのコンポーネントがズラズラ並んでメンテナンスするのが大変になってきます。ダイアログのような、一時的にそのときだけ必要になる UI・コンポーネントは他の GUI プラットフォームでは「揮発性の現象」と呼ばれているようです。こういった UI・コンポーネントは、それが実際に画面に表示される時間と合わせて、必要になったタイミングでコンポーネントツリーにコンポーネントインスタンスが追加され、役目を終えたら削除されるのが理想です。

Vue.js では、以下で取り上げる API を利用することで上に述べた内容を実現することができます。

まず、動的にコンポーネントツリーにコンポーネントインスタンスを追加するところから。次のコードは、コンポーネントのオプションの一部で、ボタンが押されたときに実行されるメソッドが定義されています。

{
  methods: {
    openDialog: function () {
      let fooDialog = new FooDialog().$mount();
      this.$el.appendChild(fooDialog.$el);
    }
  }
}

エントリとなる要素を別で用意しておいて、マウントさせることもできます。( refs: https://jp.vuejs.org/v2/api/?#vm-mount )。

new FooDialog().$mount(el);

ダイアログで表示するデータの設定は、props で渡すか( https://jp.vuejs.org/v2/api/#propsData )、ダイアログを開くためのメソッドをダイアログのコンポーネントに定義して、その呼び出しの引数で渡す等、やり方は色々あります。 生成したコンポーネントインスタンスコンポーネントツリーに追加される = ダイアログの表示と考えてよければ前者で、表示のタイミングをもう少し制御したい場合は後者、というようにアプリケーションに応じて都度判断する必要があります。

コンポーネントインスタンス生成時の propsData オプションは、コンポーネントユニットテストのために用意されたオプションのようなので、本来想定されているケースとは異なる使い方かもしれません。バージョンアップのときに書き換えが発生する可能性があることを理解した上で使うのが良さそうです。

ダイアログの操作のハンドリングは、Vue.js のイベントインターフェイス( refs: https://jp.vuejs.org/v2/guide/components.html#%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%A8%E3%81%AE-v-on-%E3%81%AE%E4%BD%BF%E7%94%A8 )を使うか、props にコールバック用の関数を渡して操作が完了したタイミングで呼び出してもらう( https://jp.vuejs.org/v2/api/#propsData )等、これもやり方が色々あります。これに関しても、アプリケーションに応じて適切だと思う方を選べばよいでしょう(悩ましい場合は、おそらくどちらでも問題ないので、好きな方を選べばよいでしょう)。

Vue.js のイベントインターフェイスを利用する例

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
fooDialog.$on('click', (event) => {
});

// コンポーネントインスタンス内
this.$emit('click', event);

props にコールバックを渡す例

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
let fooDialog = new FooDialog({
  propsData: {
    onClick: function (event) {}
  }
});

// コンポーネントインスタンス内
this.$props.onClick(event);

ダイアログの操作が完了したら、$destroy ( refs: https://jp.vuejs.org/v2/api/#vm-destroy ) で破棄して、コンポーネントツリーから除きます。

// コンポーネントインスタンスを生成した親コンポーネントインスタンス
fooDialog.$destroy();
fooDialog = null;

といったかんじで、Vue.js で揮発性の現象を扱うための API を見てきました。ダイアログ等を実装するときの参考にしていただけたらと思います。

逃げるは恥だが役に立つ ( 9 ) を読んだ

最終巻を読んだ。

最終巻の見どころは、百合ちゃんがポジティブモンスターに「呪い」について説く場面だろう。
この「呪い」は個人の行動や考え方を縛り付けている何かを指す。思い返せば、逃げ恥の登場人物は、それぞれが何かの呪いにかかっていた。平匡は女性と親しくなることを避けようとする呪い、みくりは自分を小賢しいと貶める呪い… などなど。

それぞれの呪いが、ライフスタイルを変えて、勇気を持って最初の一歩を踏み出すことで解けていく。そんな物語が逃げ恥だったのではないか、という気持ちのする終わり方だった。

BroadcastChannel API を使ってみた

BroadcastChannel API を使ってみた。

同一オリジンのタブ・ウィンドウ・フレーム間でデータを送受信できる APIAPI の詳しい説明は BroadcastChannel API: A Message Bus for the Web  |  Web  |  Google Developers に譲ります。コードは、パッと見、Cross-Document Messaging とほぼ同じですね。
サポートしているブラウザは、Blink 系のブラウザ( Chrome, Opera )と Firefox のみ( http://caniuse.com/#feat=broadcastchannel ) なのでまだ実戦では使っていないのですが、先日、個人的に便利に使える場面がありました。

先日、プレゼンをする機会があったんですが、プレゼンに使ったツールが https://github.com/ahomu/Talkie というブラウザで動くツールで(このツール、Markdown で書けて、デフォルトでけっこう良い感じに出力してくれるのでオススメです)、そのとき、諸々の事情で手元のPCの画面のブラウザウィンドウで表示しているスライドの内容を、スクリーンで映している別のブラウザウィンドウのスライドに共有・同期する必要がありました。始めは WebSocket を使おうかな、と思っていたのですが、「いや、そういえば BroadcastChannel API ってのがあったよな。まさしくこういうときに使えるのでは」と思い、試しました。

手元で開いているスライドでは、以下のスクリプトを実行して、Talkie でスライドを切り替わったときに現在のページ番号をメッセージとして送ります。

var channel = new BroadcastChannel('talkie');

talkie.changed.subscribe(function(current) {
  channel.postMessage(current.getAttribute('data-page'));
});

スクリーンで映しているスライドでは、以下のスクリプトを実行して、メッセージを受け取り、表示するスライドを同期させます。

window.onmessage = function (e) {
  // 実戦で使う場合は、どの Origin から送られたデータか等チェックしなければいけない
  talkie.jump$.next(e.data);
};

こんなかんじでやりたいことが簡単に実現できました。

実戦では、以下のような仕様を実現するために使えそうです。

  • 動画や音声を再生するサービスで、再生中に別のウィンドウで開いて再生を開始したら、自動で停止する (YouTubeSoundCloud がやっている)
  • 商品にお気に入りをつけることができるサービスで、お気に入りをつけた際に他のウィンドウ・タブに最新の状態を送信する。あるいは、最新の状態に更新するよう促す

こういった仕様を BroadcastChannel API を使わず実現するには、WebSocket を使う、あるいはブラウザだけで完結させるには、LocalStorage や cookie のようなストレージを使って、あらかじめストレージの内容を監視させ、通知を行う際にストレージにデータを書き込むといった実装を行う必要があります。自分も過去にやったことがありますが、実装が面倒な上に監視を setInterval/setTimeout によるポーリングで実現するので、パフォーマンス上の懸念もあります。 BroadcastChannel API でこういった仕様が簡単に実装できるようになるのは、ありがたいですね。

Vue v1.x から v2.x へアップデートした

Vue v1.0.15 から最新の v2.2.4 へアップデートしたのでメモ。かかった時間は2日程度だった(ついでにやった Browserify から Webpack への移行も含む)。
全体を通して、内部実装は大きく変わったが API 自体は v1 とある程度互換性があるので、vue-migration-helper の指示通りに変更して、テンプレートのエラー(正確にはコンパイル後の render 関数のエラー)を直せば、後はそのまま動くという印象。

手順

vue-migration-helper の警告メッセージに対応する

自分が対応した警告メッセージは以下のもの。大体は、エディタの置換機能で対応することができた。

transition コンポーネントをラップするコンポーネント

コンポーネントオプション

export default {
    name: 'foo-transition',
    props: [],
    methods: {
        enter(el, done) {
           // ...
        },
        enterCancelled(el) {
           // ...
        },
        leave(el, done) {
           // ...
        },
        leaveCancelled(el) {
           // ...
        }
    }
};

テンプレート

<transition
    v-on:enter="enter"
    v-on:enter-cancelled="enterCancelled"
    v-on:leave="leave"
    v-on:leave-cancelled="leaveCancelled"
    v-bind:css="false"
>
    <slot></slot>
</transition>

使うときは以下のように。

<foo-transition>
  <p v-if="bar">Hi!</p>
</foo-transition>

エラーをひたすら直す

一通りページを見て、発生しているエラーを片っ端から直した。
内部実装が大きく変わったので、以前はエラーにならなかった未定義のプロパティ・メソッドへの参照がエラーになった。
null チェックも厳しく行う必要があった。以前は以下のテンプレートで問題なかったが、

<div v-if="foo">
  <p v-if="foo.bar"></p>
</div>

このように書き直す必要があった。

<div v-if="foo">
  <p v-if="foo && foo.bar"></p>
</div>

v-if や v-show でロジックがテンプレートに漏れがちなので、メソッドや算出プロパティに置き換える良い機会なのかもしれない。

その他、あらかたエラーを直した後に Integration 環境に deploy して、社内の JavaScript のエラー集約の仕組み(Bugsnag や Newrelic にもあるやつ)でエラーが起きていないか確認した。
また、QA/テストエンジニアの人にリグレッションテストを依頼して、一通り問題がないか確認してもらった。

Vue(ライブラリ)のバージョンアップのために普段気をつけていること・思っていること

普段、なにとなく意識していることを言語化しておきます。

  • (当たり前な気がするが)ガイドや API ドキュメントをよく読んで、その API が提供されている理由を理解した上で使う。
    • たまたま動くからといって、意図されていない形では使わない。真っ先に壊れる
    • Vue.nextTick や setTimeout(fn, 0) で実行を遅らせて動いているコードも壊れやすい
  • 内部実装に近いフック・フラグ・API の使用をできるだけ避ける
    • 今回の v1 → v2 のように内部の構造が変わると影響を大きく受ける
    • ガイドや API ドキュメントを読むと内部実装に近い API は、プラグインやライブラリ開発者のためのもので一般的に使用を推奨していない記述(あるいは雰囲気)がある
  • ちょっとした便利なオプション・エッジケースで必要になるオプションも避ける
    • メジャーバージョンアップに伴う整理で廃止される可能性が高い
  • 公式ではない Vue に依存した UI コンポーネントライブラリもなるだけ避ける
    • よほど広く使われているものでなければ、メンテナンスされない可能性が高い(GitHub の Star の数や Contributers の数で判断)
    • コードを読んで、4, 500 行以下であれば、実装を参考にさせてもらい自分で書く
    • 開発スケジュールとの兼ね合いで使う場合は、最終的に自分で引き受けるぐらいの気持ちで使う
  • ビルドツール・モジュールバンドラ等と強く依存する機能の使用には慎重になる
    • v2 だと Webpack と連携する機能が次々と追加されていて、使うメリットもあるが、Webpack に依存する分、Webpack が捨てづらくなるデメリットもあることを理解する
  • 悩んだら「置換や書き捨てのスクリプトでなんとかできるか」で思考実験する
  • 悩んだら「他のツール・ライブラリで置き換えることができるか」で思考実験する

こんなところです。

デヴィッド・リンチ監督作品 TWIN PEAKS FIRE WALK WITH ME を見た

TWIN PEAKS FIRE WALK WITH ME (邦題: ツイン・ピークス ローラ・パーマー最期の7日間)を見た。

ドラマ版を見たので( ドラマ版 ツイン・ピークス を見た - kitak blog )、次は映画版。ドラマ版の前日譚。ドラマ版のネタバレがふんだんに入っているので、ドラマ版を見る予定の人は先に見ないことをおすすめします。

全体的に、特に異世界が絡んでいる部分は意味不明。意味不明なのだけど、音・映像の演出に惹き込まれていく。特に、前半にデヴィッド・ボウイ演じるフィリップ・ジェフリーズ捜査官が出てくるミステリアスなシーンが、たった数分なのだけど凄まじかった。ガルモンボジーアってなんなんだ…

この映画、ツイン・ピークスのドラマ版を見たことがない人からすれば意味不明で、見たことがある人からすればドラマ版の謎を解決しきれていない物足りなさから、世間の評判は高くないらしい。自分は、5月から始まる続編のための布石と思って見たらなかなか楽しめた。

生きていると「X は 善/悪/良い/ダメ」といった意味付けを目にしたり、あるいは自分で意味付けをおこなっている。もちろん、意味付けを行うことは、なにかを決断・進行する上で必要なことだが、時にはその意味付けが一つしかない絶対のものだと思い込み、小さい、歪んだ枠に囚われ、思考停止に陥ることもある。 マルホランド・ドライブのときもそうだったが、デヴィッド・リンチの作品の良さは自分の先入観や思い込みと向き合えることにある。デヴィッド・リンチの映画は様々な解釈ができる曖昧なシーンが多い。
シーンをどう解釈するか、意味付けるか、それらを結合してストーリーを作り上げるかは見る側にある程度委ねられている。見る側の頭の中でそれらの内のひとつを選択しているに過ぎない。その事実を教えてくれる。

Vue.js Tokyo v-meetup="#3" で発表しました

発表しました。

vuejs-meetup.connpass.com

発表

去年の Vue.js Advent Calendar で書いた記事( Vue.js 2.0 の算出プロパティ周辺のコードリーディング - kitak.blog )の内容について、@kazupon さんに「話しませんか?」と声をかけていただいたのがきっかけでした。時間が10分なので、スライドは記事からだいぶ絞った形になりました。

資料は https://kitak.github.io/slides/170316-vue-meetup/ です。

発表のモチベは、資料の「なぜ、Vue.js の内部について知るのか」(https://kitak.github.io/slides/170316-vue-meetup/#4) に書かれているパフォーマンスの低下や不可解なバグを防ぐ・解決するためにブラックボックスの中身を知ろうというところです。
特に算出プロパティは、(一応 Guide で説明がされているのですが)非同期処理やDOM操作のような副作用のある処理と併せて使ってハマるというのを自分も含め、周囲で見かけたので、今回の題材として扱いました。

次回は Virtual DOM まわりの実装について話せたら良いな、と思っています。

感想

meetup に3回通して参加しているんですが、一年前の第1回と比べて、Vue.js の人気が高まっているのを肌で感じることができました。
自分、なかなかの人見知りなんですが、スピーカーとして参加すると、懇親会で声をかけていただいたりとか自然と参加者同士で話すことができてよいですね。 あと、100人規模の勉強会で発表するのは今回初めてだったので、良い経験になりました。
発表の機会をいただきありがとうございました。