Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
vueで中規模以上のフロントエンドを組んでいて 役に立ったtips
Search
Nkowne63
January 28, 2020
Technology
5
3.1k
vueで中規模以上のフロントエンドを組んでいて 役に立ったtips
自社サービスを構築する際に実施した施策の簡単な解説です
Nkowne63
January 28, 2020
Tweet
Share
More Decks by Nkowne63
See All by Nkowne63
TypeScriptのコード生成をつらくしないために
neutron63zf
1
610
2020-11-05-side-effects-composition__1_.pdf
neutron63zf
1
400
20200128_nkowne63
neutron63zf
0
32
Vueで「見た目」「振る舞い」を分離してみよう
neutron63zf
0
560
20190523_nkowne63zf_1.pdf
neutron63zf
0
390
「つなぎこみ」を自動化する
neutron63zf
0
460
for文禁止縛り in JS
neutron63zf
0
680
Other Decks in Technology
See All in Technology
Zennのパフォーマンスモニタリングでやっていること
ryosukeigarashi
0
180
SSMRunbook作成の勘所_20241120
koichiotomo
3
160
インフラとバックエンドとフロントエンドをくまなく調べて遅いアプリを早くした件
tubone24
1
430
TanStack Routerに移行するのかい しないのかい、どっちなんだい! / Are you going to migrate to TanStack Router or not? Which one is it?
kaminashi
0
600
Amplify Gen2 Deep Dive / バックエンドの型をいかにしてフロントエンドへ伝えるか #TSKaigi #TSKaigiKansai #AWSAmplifyJP
tacck
PRO
0
390
TypeScriptの次なる大進化なるか!? 条件型を返り値とする関数の型推論
uhyo
2
1.7k
なぜ今 AI Agent なのか _近藤憲児
kenjikondobai
4
1.4k
ノーコードデータ分析ツールで体験する時系列データ分析超入門
negi111111
0
420
【令和最新版】AWS Direct Connectと愉快なGWたちのおさらい
minorun365
PRO
5
760
IBC 2024 動画技術関連レポート / IBC 2024 Report
cyberagentdevelopers
PRO
1
120
OCI Security サービス 概要
oracle4engineer
PRO
0
6.5k
TypeScript、上達の瞬間
sadnessojisan
46
13k
Featured
See All Featured
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Agile that works and the tools we love
rasmusluckow
327
21k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Reflections from 52 weeks, 52 projects
jeffersonlam
346
20k
BBQ
matthewcrist
85
9.3k
Art, The Web, and Tiny UX
lynnandtonic
297
20k
How STYLIGHT went responsive
nonsquared
95
5.2k
A designer walks into a library…
pauljervisheath
204
24k
5 minutes of I Can Smell Your CMS
philhawksworth
202
19k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Transcript
vueで中規模以上のフロン トエンドを組んでいて 役に立ったtips
自己紹介 張 たいよ (GitHub: @neutron63zf) 東京大学理学部物理学科 4年 • ventus-inc ◦
JavaScript ( Vue.js / Nuxt.js ) ◦ Golang / Firebase • (元)東京大学五月祭常任委員会 ◦ AWS / Nginx / Docker ◦ Node.js ( Express )
作っているサービス(whooop!) • スポーツチームの発行したカード を買うことで応援できる • カードを買うことでイベントなどの さまざまな特典が得られる • オークションなどで、ユーザー間 でデータをやりとりできる
構成 • 動機 • 通信の一本化 • vuexの構成 • 各コンポーネントとデータフロー •
実際にやってみて・まとめ
前提知識 • Vue ◦ 表示をコンポーネントという単位に分割して書くことができる • Vuex ◦ (すごくざっくり言うと) Storeというものに状態をまとめ、それに対して操作をする。
◦ 「一方向のデータフロー」を課すことで、挙動を予測しやすくする。 • Nuxt ◦ Vueでサーバーサイド・レンダリング( SSR)をするときに便利なフレームワーク ◦ ほとんど普通の Vueを書く感覚でSSRができる反面、制約がきついところも
動機:「いつの間にかアプリケーションが大きく...」 • whooopは当初、ページは20ほど、エンドポイントは40ほどのサービスだった。 • しかし、いつの間にか機能の増加とともに、ページ数もエンドポイントも倍以上に! ◦ 現在はエンドポイント 100近く(統合中...)、ページ数50ページ程度 • 当初のコードではだんだん改良、機能追加が厳しくなっていった
アプリケーションが想定を超えて大きくなり、当初の書き方だと通用しなくなった
例(1): リクエスト処理が不明確 • APIで通信する際、上はuserIdを与えればいいということ がわかるが、下はAPI ドキュメントを読まないと、何を与え るべきか不明 • 生でHTTP Methodやurl、パラメーターを書いていて、そ
れが至るところにあるので、変更が大変
例(2): データ変換・保持がまちまち • teamsとteamlistみたいに、名前がかぶっていたり、通信 後にキャメルケースに変換しているか否かがまちまち • どのようなデータ構造でストアに保管されているかはスト アごとに異なり、新しく追加されるものについても、「今ま でのステートに統合される」のか「今までのステートを上 書きする」のかが異なる
結論:しんどい • ストアが使いづらいことこの上ないので、ストアのステートからではなく、毎回ストアのアクションの返り値を 直接コンポーネントにセットするコードが多発 ◦ ストアを使う意味 ... • 上と並行して、ストアにデータを記録することなくレスポンスをパースするだけのコードも多発 •
たまに変なデータ変換をかけている部分は逆変換をかけないと使えないことも →デザインのリニューアルついでに大幅にリファクタリングするぞ! (このスライドはその時にいろいろ試した施策をいくつか抜粋して紹介している)
通信の一本化
△通信したい ◦通信して〇〇したい そもそもフロントから通信をするときに、いちいち postかgetかput か。urlはどこか、どこのパラメーターにどのデータをどんな形式で 入れるかは気にしたくない。 そういうのはいちいち意識せずに済むように、 関数でリクエスト形 式等の内部構造はある程度ラップする。 ラップした関数は集めておいて、そこからだけ使うようにする。
呆れるほど単純だが、これを徹底するだけで、そうでない場合に 比べてかなり見通しはよくなる。
APIドキュメントから自動生成 APIの数が増えてくると、単純に先程のようなコードを錬成するの がめんどくさくなってくるので、 Open APIや、API Blueprintといっ た、ある程度まとまったAPIドキュメントがある場合は、そちらから APIクライアントを自動で作ると楽。 例えば、Swaggerの場合はSwagger-Clientから、自動でSwagger ドキュメントを読み込み、APIクライアントを動的に作れる。
(一瞬でつなぎ込みが終わりちょっとした全能感に浸れる)
Vuexの構成
APIデータ保持ストアの分離 • propsのバケツリレーの緩和 • ページをまたいだ状態の保持 • APIから取得したデータの保持 最初の2つは、ユースケースごとにモジュールを作ってもそんなに荒れな い。 しかし、通信(つまり3つ目)は統一されていたほうがアプリケーションが作
りやすいのでちゃんと考える。
ネストされたデータの展開(1) APIリクエストを扱っていると、あるオブジェクトのプロパティーに関連 するオブジェクトが入っていることがよくある。(例: articleのcomments プロパティーに、commentというオブジェクトが配列で入っている) だが、normalizrというライブラリを使うと右のように、オブジェクトの種 類ごとに展開でき、「key-value」ごとにアクセスできる形式に変換でき る。
ネストされたデータの展開(2) これにより、オブジェクトの種類ごとにストアを作成し、 normalizrに より分解されたデータを入れることにより、ストアの中のデータの入 り方を統一することができるようになる。 なお、normalizrでは、何も指定しないとidでしかアクセスできるよう にならないが、他のキーでもアクセスしたい場合は、そのキーと idの 対応表を作成しておくとよい。
APIストアでやること 以上のことをまとめると、APIストアでやることは以下の通り。 • (初期化時)オブジェクトの種類だけ配下のモジュールを動的に登録する。 • リクエストのアクションがdispatchされたら、通信を実行 • レスポンスをnormalizrにかけて、各モジュールに分配
APIストアでやること(実際のコード例) 2行目でリクエストを実行し、 3行目でnormalizrにかけて、 5行目でストアに保存
(余談)リクエストを送るメソッドの追加 whooopでは、リクエストを送る際はストアのリクエストアク ションをディスパッチすればいいようにしてあるが、毎回 「this.$store.dispatch」、なんて書いているのは正直めんど くさい。 なので、vueのインスタンスに「$apiRequest」を、nuxtのコ ンテキストに「apiRequest」をプラグインし、それでリクエスト が送れるようにしてある。
viewに依存するストアの部分 基本的に注意すべき部分はあまりないが、以下の 2ルールだけ課した。 • ステートは最低限必要なものにすること(何でもかんでもつめこまない) ◦ ページをまたがって引き継ぐ必要のあるデータ。そして、 props渡しだと不可能なデータの運搬(モーダルなど) ◦ そうで無いデータはコンポーネントのステートとして処理できるので、あまりストアの旨味がない
• アクション・ミューテーションも最低限必要なものにすること ◦ つまり、複数のアクションのうち共通する処理であっても、ストアの内部でしか使われないのならば、「アクショ ン」や「ミューテーション」ではなく、関数として切り出す 特に2つ目のルールは、ストアが大きくなったときにスパゲッティーコード化することを多少は緩和してくれる。
各コンポーネントと データフロー
Atomic Designを導入する(1) デザインをリニューアルする際に、「 Atomic Design」 を導入し、コンポーネントの再利用性を高めることにし た。 だが、右の図にあるような「よくネットである Atomic Design」の他にも、会社ごとにたくさんの「オレオレ
Atomic Design」なるものがあり、どれもよさげで迷っ た。 結局、厳密でなくても、「階層的にコンポーネントを構 築していく」というコンセプトの元で自分たちなりにルー ルを決めることにした。
Atomic Designを導入する(2) 悩んだ末、「Templates」の代わりに、「Layouts」という 層を「Organisms」と「Pages」の間に置くことにした。 そして、以下のルールを課した。 • それぞれの層のコンポーネントは、それよりも 下層のコンポーネントだけから作られること。 • Organisms以上の層では、CSSを基本的には
書かず、クラスをつけるなどで対処する。 代わりに 「Layouts」 を入れる
CSSとAtomic Design Vueでは単一ファイルコンポーネント内で CSSを書くこと ができる。 だが、Organism層以上では基本的にはそれを使用せ ずに、位置の微調整などはクラスをつけることで行っ た。(例:flexボックスにしたい場合はflexクラスをつけ る) これにより、CSSの重複などを防ぐことができるほか、
コーディングの指針が明確になる 。(CSSがどうしても欲 しいなら下の層にうつすか、新たにクラスを生やす。)
どの層かの判断基準 Atomic Designでおそらくいちばん困るのが 「このコンポーネントはどの層に属するのか」という判断 で、実際 に運用をしたい場合はここを ある程度スムーズに判断できるような基準を設けておく とかなりやりやすい。 whooopでは現在以下のように区分けしている。 •
Atoms … これ以上分解すると機能として成り立たない単位(チェックボックス等) • Molecules … 特定のオブジェクトに依存しないが、 Atomsを複数組み合わせて実現できる単位(カ ルーセルや、検索窓等) • Organisms … 特定の種類の、単一のオブジェクトを表示する単位(ユーザーの持つカード等) • Layouts … 複数種類や、複数個のオブジェクトを表示する単位(ユーザーの持つカードの一覧)
OrganismsとLayoutsの例 右の画像は「チームの中で、どれくらいカードを集めている かのランキング」という「Layouts」 その中の1行1行が「ユーザーが何位で、何枚カードを持っ ているか」を表示する「Organisms」
コンポーネントによるリクエストの構築 vueのコンポーネントがbuildRequestというオプションを持 てるようにして、リクエストを各コンポーネントで構築できるよ うにした。 これにより、「使っているコンポーネントにそぐわないリクエス トを送ってしまう」という事態をかなり減らせる。 なお、そのまま送らないのは、 nuxtのSSR時は、基本 pagesからしかリクエストを送れないため
コンポーネントによるリクエストの構築(2) VueのScoped Slotsという機能を用いてリクエストを送っ て、子のコンポーネントに送るだけのコンポーネントを作る と いう手法もかなり有効。 これにより、getリクエストに関しては宣言的に書くことができ るだけでなく、loadingやerror時の分岐など、状態に由来す る処理を共通化することができる。 「ダミーデータで表示している部分で実際に
api由来のデー タを使うようにする」という書き換えが一瞬で終わる。
ストアのデータの取得(1) サーバーから取得したデータはストアに入っているわけだ が、そのままでは取得するのが少し面倒。 そこで、オブジェクトの種類ごとに 「idや、idの配列から簡単 にストアをクエリできる関数」 を作っておくとストアから取得す る部分が随分すっきりする。 whooopでは、(normalizrのために定義した)オブジェクトの 一覧から、ストアだけでなく、こうしたヘルパー関数も自動で
作っている。
ストアのデータの取得(2) また、normalizrを通すとネストされたデータは idに変換され てしまうので、ヘルパー関数でそれらを再構築できるように するオプションも実装した。(右の画像では、ストアからオー クションの配列を取得し、ordersというキーについて、またス トアから注文の配列を取得し、付加している。) とはいえ、これを使わずに実装できるケースがほとんど。
実際にやってみて ・まとめ
実際にやってみて(1) 快適!! フロントエンドで面倒になりがちな • CSS • つなぎ込み の2つの負担をかなり減らせる。そして機能に集中してコードを書くことができるようになる。 それなりに準備は大変だったがおすすめできる。
実際にやってみて(2) 一方で、Atomic Designはともかく、リクエストを送る部分はかなり「オレオレフレームワーク」になってしまった 感が否めない。(通常のvue, vuexの上に積み上げたものが大きい) そのため、はじめて入ったメンバーに対しては、説明をした上で、比較的小さな、しかしこれらを一通り経験で きるようなタスクを振ることで慣れさせる期間を設けている。
まとめ • APIリクエストは、http methodなどをラップして一箇所にまとめておく • APIデータをストアに出し入れする方法は用意しておくと楽 • 表示などで使うストアは、ステートもアクションもミューテーションも最低限に抑える • Atomic
Designをやるなら階層を分ける基準は明確にしておいた方がいい • コンポーネントがリクエストを構築できるようにしておくとよい