Misskey: ダイアログ系コンポーネントのリファクタリング

Created on 19 Jul 2020  ·  15Comments  ·  Source: syuilo/misskey

Summary


Vue3移行する際にもこの改修が必要になる。

「出すのが親なら、消すのも親」という理念に則り、現在 $root.new で動的に作成しているコンポーネント、いわゆるダイアログ系のコンポーネントをVuexのStateで表現するようにし、ルートのコンポーネントが、VuexのStateを見てそのコンポーネントをレンダリングするようにする。
これに則れば、自壊するコンポーネントは存在しないことになり、例えば閉じるボタン付きのダイアログやウィンドウでさえ、閉じるボタンが押された時にコンポーネントを実際に閉じるのはコンポーネント自身ではなくあくまで親ということになる。

ダイアログを例に一連の流れを解説する

  1. ダイアログ呼び出し側は、Vuexにダイアログ表示アクションを発行する。
  2. Vuexはアクションを受け取ると、ダイアログ一覧stateにダイアログ情報を追加する。その際、ダイアログ情報には結果を格納するためのresultプロパティも入れておく。
  3. 呼び出し側はそのresultのミューテーションをsubscribeしておく。
  4. ルートコンポーネントは、Vuexのstateに応じてダイアログをレンダリングする。
  5. ダイアログはボタンが押されるなどしたら、そのイベントをemitする。
  6. ルートコンポーネントは、イベントを受け取ると、Vuexのresultプロパティを更新するアクションを発行する。
  7. ミューテートされたので、呼び出し側はresultの変更を知る。
  8. 呼び出し側は最後に、ダイアログを閉じるアクションを発行する。
  9. Vuexはダイアログ一覧からそのダイアログ情報を削除する。
  10. ダイアログ情報が消えたので、ルートコンポーネントはダイアログのレンダリングを終了しダイアログコンポーネントは閉じる。

このリファクタを行った暁には、destroyDomも消せるはず。

Sequence diaglam

Untitled

actor User
User->UI: Click a button etc
UI->Store: Dispatch "showDialog" action
Store->Store: Add dialog state
Store->UI: Render dialog as open
UI->User: See dialog
User->UI: Click the dialog
UI->Store: Commit "dialogDone" mutation
Store->Store: Update dialog result state
Store->UI: Render dialog as closing
UI->UI: Animate for close
UI->Store: Commit "dialogRemove" mutation
Store->Store: Remove dialog state
Store->UI: Render dialog as closed
UI->User: See the dialog closed
💚Refactor 🖥️Client

All 15 comments

ただ、AiScriptとかグローバルに動くようなものからの呼び出しでない限りは、Vuex通さずに呼び出し側のコンポーネントのローカルステートでダイアログ管理した方が良さそう。
それなら、呼び出し側コンポーネントが(ページ遷移などで)消えたらダイアログも自動で消えることになり都合が良い。

...とは言ったものの、いろんなコンポーネントでそういうダイアログ用ステートを持って管理するというのも煩雑になりそうで悩みどころではある

ダイアログ系のコンポーネントをVuexのStateで表現するようにし、ルートのコンポーネントが、VuexのStateを見てそのコンポーネントをレンダリングするようにする。

破綻しそうな感じがするわね……

ダイアログ系のコンポーネントをVuexのStateで表現するようにし、ルートのコンポーネントが、VuexのStateを見てそのコンポーネントをレンダリングするようにする。

破綻しそうな感じがするわね……

というと?

いやまあ、感覚的なアレなんですが、Stateでの表現の仕方を上手にやらないと色々面倒なことになるかなーと。

単に今Dialogにプロパティとして渡している情報 + 結果情報
で良いと考えてる

ルートに追加して何か応答を貰うのはダイアログだけではなくほかにもありそう(メニューとか?)だけど、それぞれに対してそういうstateを追加するということ…?

そうなるね

多分それがVue的に正しい実装だと思うけど、なんかスマートじゃない気がするんだよなぁ
何が正解なのか正直分からんくなってきたけど、少なくとも今の $root.new する方法は使えなくなるので別の方法にしないといけない

state管理で不利な点をいっこ挙げるとすれば、stateが1種類に1つの値しか持てない場合、コンポーネント1種類に1つの表示しかできないことになり、設計が不自由になりそう。ただ、stateを配列にすると管理が一気に面倒になりそうな感じが。

そもそもvue3のドキュメントを読めていない(どこ?)のだけれど、$rootを触れない感じなのかしら?

stateが1種類に1つの値しか持てない場合、コンポーネント1種類に1つの表示しかできないことになり、設計が不自由になりそう。

配列にする予定

$rootを触れない感じなのかしら

それはできるけど、新しくVueインスタンスを作成するときに親が指定できなくなってそう
あと、コンポーネントの$onとか$offが削除された
そもそもこの使い方はVue2の時点でもかなりハッキー(親指定機能は本来はテストのために用意されているもの)なのでいつ使えなくなってもおかしくない

Vue3のドキュメントはここ
https://v3.vuejs.org/

vueでモーダルとかどうしてんのかなと思ってみてみたら、stateで処理してるんですなぁ…

https://jp.vuejs.org/v2/examples/modal.html

的外れな指摘かもしれませんが,Teleportが活用できそうな気がします https://v3.vuejs.org/api/built-in-components.html#teleport

ありがとうございます🥰
teleportだと、ダイアログ呼び出し側がコンポーネントである必要があって、AiScriptプラグインなどコンポーネントとしての実態を持たないコンテキストからダイアログ表示しようとしたらやはり一旦ストアを介する必要があります
ただ、前者の場合(コンポーネントから呼び出す場合)は仰るようにteleportで実装しても良いと考えていて、それが https://github.com/syuilo/misskey/issues/6562#issuecomment-660595737 ですね。
しかし↑にも書いたようにそれぞれのコンポーネントで似たようなダイアログ管理処理を書かないといけないので、ストアに集約させた方が煩雑さはなくなるかなとか色々考えてる途中ではあります

実装してるけど、最終的に設計はこのPRで説明しているものと異なる
内容の古い情報が残っていても紛らわしいだけなのでこのPRは閉じます。

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tamaina picture tamaina  ·  3Comments

tamaina picture tamaina  ·  3Comments

tosuke picture tosuke  ·  3Comments

tamaina picture tamaina  ·  3Comments

ibrokemypie picture ibrokemypie  ·  3Comments