Vuex PersistedStateの値の復帰のタイミングが謎だったのでNuxtのplugin書いて解決した

背景

先日から Vue (Nuxt) + Vuex + Vuex PersistedSate でモノを動かしてるんだけど、

なんかふいにデータが矛盾して動作がおかしくなる

ことが起きていた。

まぁアタリはついていて、Nuxtでrouteに応じてVuex Storeをmodule分割する方法 - あーありがち(2018-04-19) にある動的に module 構造の Vuex Store を生成している処理と、 Vuex PersistedState を通じて localStorage からデータが返ってくる処理がそれぞれバラバラに動いていることで、「Vuex Store の値を見て判定する処理」がタイミング次第の微妙なシロモノになっているんだろうなぁと思ってたんだけど、決定的な解決策が見つからないでいた。

通常の使い方だと問題にならないけどテストしてるとよく踏み抜くので、ごまかしごまかしやっていたのだが、今回解決できたので記録しておく。

※ ちなみに今回の話は Vuex PersistedState が localStorage の値でメモリ上のデータを書き換え終わったら Vuex がそれを通知してくれて Vue コンポーネントの computed に反映できれば、実はあまり悩まなくて済んだのだけど、対策としてやったこと自体は Vuex PersistedState を使っていなくても参考になるし、今後自分で参考にしたいので書いてます。

使っているもの

おさらい - Nuxtを利用したVueアプリにきっとありがちなこと -

  1. Vuex PersistedState 経由で localStorage から値が復帰するのは mounted() 以降
  2. this.$route や this.$store に依存するコードが安定して動くのも mounted() 以降
  3. Nuxt では基本的にアプリの全体像を気にしなくても済むように Page component を突っ込むと routing も自動解決する、逆に言うとアプリ全体を組み立てるプロセスに介入するのは難しい

これらが合わさると何が起きるかというと、

Page component の mounted() が複雑で長大な一つのメソッドになりやすく、しかもタイミング問題まで抱え込んでしまう。

これ割と地獄感あるのですよ。

解決方法

Vuex Storeの初期化はpluginで行う

plugin function からは

  • 初期化済みの store オブジェクト
  • page component の有無から自動で解決される routes オブジェクト

の両方にアクセスすることができる。したがって、Nuxtでrouteに応じてVuex Storeをmodule分割する方法 - あーありがち(2018-04-19) でやったように

this.$store.registerModule(NAME, OBJECT)

ではなく、plugin の中で

export default ({app, store} => {
  store.registerModule(NAME, OBJECT)
})

みたいにすると、Vue の root component が初期化される前にすべてを揃えることができる。

mounted()の中でsetTimeout

Vuex Store に localStorage から値が返ってくるのは mounted() 以降なのだが、値が返ってきてからでないとメモリ上の値で reset することはできない。

ということで以下のような感じになる。

mounted() {
  setTimeout(() => {
    resetの処理({値1, 値2, ..})
  }, 0)
}

あれ、結局 mounted() の中が複雑なのは一緒じゃね?と思うかもしれないが、mounted() の中で registerModule すると、localStorage から値が返ってくるタイミングも遅くなり、かなり制御が難しくなるので、

mounted より前に registerModule しておいてからの setTimeout

という扱いにしておくのがよい。また、setTimeout() の中も直接長いコードを書かずに、遅延させてるのが分かる reset 処理だけ独立させた PureJS な function として切り出して、そいつに外からバンバン値を与えてやるのがよさそう。なんなら this.$route とか this.$store とかバンバン与える。

※ なんで reset したいのかはアプリによると思うが、保存しておいた何かと比較して違ってたら何か処理を行いたい、という要求は普通によくあると思う。

まとめ

Nuxt + Vue アプリを書く際には、

  • Vue component ではない形で解決できるものはないか考え、どんどん分離する
  • plugin は root component 初期化前に async/await で動作するのでよいぞ

を覚えておくのがよさそう。

特に、意外と View に関わらないコードは多くなるので、積極的に Vue component 以外の形にして、Nuxt のディレクトリレイアウトで言う components/ つまりレールから外れた場所に置くコードを増やしてやるのが吉っぽい。

です!

[^1]

参考

vcat なんてものを作ってみた

vertical cat のつもりだけど、あれ意味反対か? horizontal か? そうかもしんない。まぁいいや。

何をするかというと、フツー

cat file1 file2 > dest

ってやるとできあがるファイルは

file1の内容
file2の内容

って繋がるでしょ? これを

file1<TAB>file2
file1<TAB>file2
file1<TAB>file2
...

ってくっつくようにしたもの。

例えば何かをフィルタで処理して結果を出力しちゃったんだけど、後から before と after を並べて印刷したい、とか思うこともあるわけですよ。そういうときにいちいちそのフィルタ書き換えて before と after 並べて出力するようにしなくても、これを通せばすぐできるよ! というやつです。

なんか気づくと cat 系のものをよく作ってる気がする。

え。コードがハイライトされてないし、何か分からない? ったく最近の若いもんは! awk に決まってるでしょ!

[追記] 各所でツッコミが。

うっうっ。以下言い訳。

  • paste という名前で動作が予想できないので、いざ使うときに名前を思い出せない
  • man を読んでも意味が分からなかった

きっとこの両方だったに違いない。

ん? あ! そうか! cut と対になってるじゃん! がーん。不覚っ。

OSC2006新潟なんてあんのか

つか去年もその前もやってたのか…。知らなかったよ。全然。

rlwrap すっげ

川o・-・)<2nd life - rlwrap - readline ラッパー

Rhino が readline 効かなくて実はちょっと試しただけでソッコーうんざりしてたんだけど、これ挟むとめっちゃ便利。なんじゃこれむっちゃ便利。なんじゃこれ。なんじゃこれ。なんじゃこれ。鼻水出たわ。びっくりした。

まぁ、今のところ個人的には spidermonkey が動くなら rlwrap + Rhino を使う理由はないし、他にこれに使いたいっつーのもあんまり思いつかない。でも覚えておこう。rlwrap !

あー。システム管理系の古いコマンドには readline 対応してないの多いな。そういうのに使うといいのか。

IE7 に望むこと

  • デフォルト文字サイズの変更

IE がばかみたいにでかい文字にしたばかりに今の Web デザインの、むやみに文字サイズを小さくしたがる(font size="-1" とか font-size: 90% とか)風潮が生まれたのだ。だから IE7 では最初からそれくらいのサイズにしちゃって、デザイナに文字サイズに変なこだわり(IE 前提で文字を少し小さめにするのがプロの常識、みたいなね)を持つのが無意味だということを思い知らせてほしい。

  • 文字サイズと画像サイズのバランスを簡単にとる機能
    1. CSS で画像と文字サイズのバランスを簡単に整える方法を独自拡張でもなんでもいいから実装して、画像の拡大縮小を超絶美しく実現する
    2. あるいは Opera の拡大縮小の機能をパクる

文字サイズについてのもう一つの問題は、「画像サイズがピクセルで固まってしまうからバランスをとるために文字サイズも固定したくなる」というやつだ。(幅固定のいわゆるソリッドデザインなんかも根っこはこれと同じ。)これは「文字をユーザーが拡大縮小できても画像がついてこないからバランスが悪くなる」→「これを防ぐために文字サイズも px で固定しちゃえ」という論法である。これはある意味とても正しい。バランスの調整はとても大切で、バランスと色で雰囲気はあらかた決まってしまうと言っても過言ではない。

だから画像のサイズを例えば縦2行分、横10文字分などでサイズ指定して、それに合わせた拡大縮小を超絶美しく実装してくれるといいんじゃなかろうか。ラスタ画像でそれをやるのが難しければ、SVG 完全対応だ。(そうすればドット絵の雰囲気を残しながらの拡大縮小も可能だ。)

客が悪いでしょ

米Hotmail、顧客ファイルを消失 (CNET Japan)

いくら特殊な例とは言え、分散して置いておかない方が悪いでしょ。Hotmail に対する自分の心象を差し引いてもデータのバックアップを行うのは当たり前で、それをしていなかった方に落ち度があるだろう。素人がおぼつかない作業でバックアップを行うより、専門家がサーバサイドで行ってくれるバックアップの方が信頼できると思うかもしれないが、それがあってなおかつ自分がバックアップを取る方が望ましいのは言うまでもない。と思ってるのはもしかして少数派?

付け加えるなら、自らは何もコストを負担せず(Hotmail だから spam の受信などのコストは払っているかもしれないが :-)に何かが保障されるなどということはあり得ないと考えないとね。

cocolog 遅い

予想していたことではあるが、なんで夕方の更新でこんなに遅いのか。

blog クライアントもいくつか見たがゴテゴテしすぎかシンプルすぎの両極端で、どうもフィットしてこない。結局 w3m, Emacs, PukiWiki, tDiary が揃っている方が使いやすいじゃないか。確かにセットアップには時間掛かるが、一度セットアップできればリモートでも普通に使いものになるし。

もう少し

  • 満足に日本語の使えるクライアント
  • TypePad(ココログ)でも使えるクライアント

が増えてこないとまだ結論は出せないだろうけど、今のところ「blog ってこんなもんか?」という感じは否めない。まぁ確かにオタクくさい日記 CGI じゃなく、デザインもなかなかハイセンスな感じ(横幅固定はいただけないが)なので、一般ウケはするのかもしれないけど、ハマるほどいいものだろうか、というのが正直な感想。

買う

AirMac Express

無線LANカードを持ってないので調査。

PLANEXGW NS54GMX3000円

PLANEX 製品はどうしてこんなに安いんだろう?

と思ったら Windows のみ、WPA は XP のみ、かぁ。

8号線ヤマダ電機には 4000円のものがあった。

blog クライアント

  • ecto
    • .NET 入れたが全然満足に動かず。2週間試用可のシェアウェアだけど、要らないかな。MacOS 版の評判はすこぶるいいみたいなんだがなぁ。
  • Miech
    • IEコンポーネントタブブラウザ + RSS リーダ + blog エディタ
    • blog エディタとしての使い勝手はイマイチかなぁ。RSS リーダ部の存在感が強すぎる。
  • blogBuddy
    • なんか https の通し方が分からないのか、TypePad に対応してないのか、とにかく blog 情報の取得できず。シンプルでよさげなんだけど。
  • glucose
    • Python だ。Python を使った IE コンポーネント RSS リーダ + blog エディタ。RSS リーダでニュースをチェックしながら自分の blog にそのコメントを書く、という流れが実にスムーズに行える。
    • blog の設定は cocolog の api の方の uri ではなく blog そのものの uri にする。
    • Miech と同じようなコンセプトなんだけど、作りはこっちの方が好みかも。デフォルトの設定は Miech の方が丁寧というか、RSS などの準備が丁寧なので、いきなり使い始めるには Miech の方がいいかなぁ。
  • Slug
    • シンプルすぎて何が何やら。

しかしあれだな。RSS リーダーとブラウザが一緒になってるやつはとにかく場所取っていかんな。ブラウザ部分にテキストブラウザを利用するとか、そういうことはできないのかなぁ。

About

例によって個人のなんちゃらです