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
630
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
PHP ユーザのための OpenTelemetry 入門 / phpcon2024-opentelemetry
shin1x1
1
420
Snykで始めるセキュリティ担当者とSREと開発者が楽になる脆弱性対応 / Getting started with Snyk Vulnerability Response
yamaguchitk333
2
190
なぜCodeceptJSを選んだか
goataka
0
160
LINEスキマニにおけるフロントエンド開発
lycorptech_jp
PRO
0
330
OpenAIの蒸留機能(Model Distillation)を使用して運用中のLLMのコストを削減する取り組み
pharma_x_tech
4
570
TSKaigi 2024 の登壇から広がったコミュニティ活動について
tsukuha
0
160
多領域インシデントマネジメントへの挑戦:ハードウェアとソフトウェアの融合が生む課題/Challenge to multidisciplinary incident management: Issues created by the fusion of hardware and software
bitkey
PRO
2
110
KnowledgeBaseDocuments APIでベクトルインデックス管理を自動化する
iidaxs
1
270
ガバメントクラウドのセキュリティ対策事例について
fujisawaryohei
0
560
NilAway による静的解析で「10 億ドル」を節約する #kyotogo / Kyoto Go 56th
ytaka23
3
380
第3回Snowflake女子会_LT登壇資料(合成データ)_Taro_CCCMK
tarotaro0129
0
200
プロダクト開発を加速させるためのQA文化の築き方 / How to build QA culture to accelerate product development
mii3king
1
270
Featured
See All Featured
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
17
2.3k
Mobile First: as difficult as doing things right
swwweet
222
9k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
What's in a price? How to price your products and services
michaelherold
243
12k
GraphQLとの向き合い方2022年版
quramy
44
13k
KATA
mclloyd
29
14k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
810
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
2
170
Build your cross-platform service in a week with App Engine
jlugia
229
18k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Code Reviewing Like a Champion
maltzj
520
39k
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をやるなら階層を分ける基準は明確にしておいた方がいい • コンポーネントがリクエストを構築できるようにしておくとよい