kitak's blog

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

逃げるは恥だが役に立つ ( 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人規模の勉強会で発表するのは今回初めてだったので、良い経験になりました。
発表の機会をいただきありがとうございました。

ドラマ版 ツイン・ピークス を見た

デヴィッド・リンチの作品を何か見たいな、ということで、5月から続編が始まるツイン・ピークスを見た。
ドラマの放送は自分がちょうど生まれた頃にやっていて、小学生の頃、軽部アナめざましテレビで映画版を紹介しているのを見た記憶がぼんやりとある。何となく惹きつけられた。

物語は田舎町で起きた美少女の殺人事件やそれに連鎖して発生した事件を解決するために FBI 捜査官ががんばる話。登場人物のキャラの濃さ、森の不気味さ、オカルト・まじないの要素が混ざって良い塩梅で面白い。
特にカイル・マクラクラン演じるクーパー捜査官のキャラが良い。あまりに美味しそうにドーナツを食べたり、ブラックコーヒーを飲むものだから、ダイエット中なのにも関わらず、僕もドーナツを食べて、熱いブラックコーヒーで流し込みたくなる。

全体的に面白いのだけど、海外ドラマあるあるで終盤はかなりグダグダ。ラストも「えー」というかんじになる。ここらへん映画編も踏まえて、続編できれいに再構築してくれるんじゃないかな、と期待している。

Vuex でのエラーの扱いについての個人的な考え

最新の dex.fm — Development at Mercari US で、Redux でのエラーの扱いについての言及があって、自分は Vuex (Redux インスパイアな Vue の状態管理ライブラリ)でどうやっていたっけな、というのを文章で簡単に書いておきます。あくまで個人の考えです。

予め、Vuex の Action は、Redux の Action とは異なることを書いておきます。Vuex の Action は、純粋な State の変更を行う前の非同期や副作用が伴う処理を指し、純粋な State の変更は Mutation でおこなっています。Redux での非同期処理の扱いについて書かれた ReduxでのMiddleware不要論 - Qiita の記事の ActionDispatcher に近いものです。

Vuex でのエラーの扱いに関しては

コンポーネント内で閉じるか否かによる

この一言に尽きます。

Vuex のドキュメントでは、ひとつのコンポーネントに閉じている State について、以下のように述べています。

Vuex を使うということは、全ての状態を Vuex の中に置くべき、というわけではありません。多くの状態を Vuex に置くことで、状態の変更がさらに明示的、デバッグ可能になりますが、ときにはコードを冗長でまわりくどいものにします。状態の一部がひとつのコンポーネントだけに属している場合は、それをローカルの状態として残しておくとよいでしょう。あなたは、トレードオフを考慮した上で、あなたのアプリの開発ニーズに合った決定をすべきです。

自分もこの考え方に賛成で、Vuex に限らず Flux アークテキチャを適用した場合、ひとつのコンポーネントに閉じている State は無理に Store で管理しなくてもよいのでは、と考えています( 複数のコンポーネントに跨った State になるときに Store へ移せばよい)。

エラーに関しても、State と同様に考えます。厳密に単方向のデータフローを守り、エラーが発生した際に Action を Dispatch するか、Mutation を Commit すれば、上記のドキュメントの引用と同様の利点がありますが、エラーが画面に表示されるまでの処理がまわりくどいものになる場合もあります。

Vuex の Action でエラーが発生したことを Action を Dispatch したコンポーネントだけが知ればよい場合は、コンポーネント内でエラーハンドリングをおこなえばよいのではないでしょうか。Vuex の Action Dispatch は Promise オブジェクトを返すインターフェイスになっているので、以下のように catch メソッドでエラーをハンドリングできます。

this.$store.dispatch('fetchSomeData').catch(() => {
  // alert を出す or コンポーネントのステートを変更する
});

Action でのエラー発生を複数のコンポーネントに跨って扱う、State を変更する場合は、Action 内でエラーハンドリングして、さらに Action を Dispatch するか、Mutation を Commit する形になるでしょう。その後、変更された State を適切なコンポーネントで扱います。

おわり