はじめまして!estieでsoftware engineerをやっているhigeです。
先日社内で行った勉強会の内容について簡単に公開しようと思います。
当日はZoomで質問を受け付けながら話をしました。
前提から
estieには多様なバックグラウンドを持ったエンジニアが集まっており、それぞれ得意とする領域が異なります。estieには2つのプロダクトがあり、片方がNuxt.jsを、もう片方がRuby on RailsにのったVue.jsを利用しており、両方でVuexを使って状態を管理しています。
今回の勉強会では、フロントを専門領域としない仲間に向けて、フロントエンドの雰囲気をざっくり伝えるためのLTをやりました。
おしながき
非フロントエンドエンジニア向けのお話なので、結構内容はふわっとしています。 間違った見解等ございましたら、ご指摘いただければ幸いです。
ふわっとしたフロントエンドの話
全くwebフロントエンドに関わってこなかった人がはじめてやってみよう!となった時には何から学ぶのでしょう? HTMLやCSSでしょうか?それとも最近流行りのReactやVue.jsといったライブラリ?Typescriptなんかもいいですね。
実のところ、最近のwebフロントエンドで学んでおきたいことは結構たくさんあります。
バーっととりあえずわかっておきたいことを挙げてみると、
- HTML,CSS,JSの基礎知識
- Webpackの使い方
- Vue.js,Reactの使い方
- 状態管理手法
- CSR, SSR, SSG……
- Nuxt.js, Next.js……
- JAMStack
- Atomic Design
- a11y, i18n
- REST, GraphQL
この辺りがわかっていればとりあえず開発はできるでしょうか。
まだまだたくさん学びたいことがありますが、今日はこの中でもestieで使っているVue.jsにフォーカスして話します。
Vue.jsの話
そもそもVue.js(以下Vue)ってなんでしょう?
公式では
The Progressive JavaScript Framework
と標榜していますが、具体的にVueは何をやってくれてどう便利なのでしょうか?
Vueとかが出てくる前の話
以前のwebフロントエンドでは、サーバサイドでテンプレートエンジンを利用してHTMLを構築し、フロントエンドでのUIの制御にJSを使うというような形が主流でした。シンプルなUIの構築についてはこれで問題ないのですが、昨今のリッチなUIを実現するためにはこれでは難しい点が出てきます。 リッチなUI、つまりブラウザ上で移り変わるようなUIを表現する場合、JSで色々とやる必要があり、それがかなり複雑なんですよね。
では、ブラウザ上で移り変わるようなUIをシンプルなJSで実装した場合どのようになるかやってみましょう。
- 何かしらのボタンをクリック
- APIから社員一覧をとってくる
- APIから値が返ってくるまでloading要素が表示される
- 社員一覧を表示する
という動的な移り変わりを実装してみましょう。
// 5秒後社員リストが返ってくる const getSyainList = () => new Promise((res) => { setTimeout(() => res(['hige', 't-poyo', 'eririn']), 5000); }) const button = document.getElementById('get-shain-list'); const container = document.getElementById('container'); // 読み込み中要素の作成 const loading = document.createElement('div'); loading.textContent = 'loading'; const handler = async () => { container.appendChild(loading); const list = await getSyainList(); container.removeChild(loading); list.forEach(e => { const dom = document.createElement('li'); dom.textContent = e; container.appendChild(dom); }); }; button.addEventListener('click', handler);
簡単なUIの移り変わりですが、多少手間がかかっているのがわかるかと思います。
面倒なのは、一つ前のDOMの状態を知っている必要があることです。上記の例ではLoadingが終わった際にloading要素を削除していますよね? つまり、刻々と移り変わっていくDOMの状態を常に把握していなければUIを制御できないということです。
一つ前の状態に手を加えるという操作を繰り返すわけですが、要するにDOM要素に対して足し算引き算を繰り返してUIを操作しているわけですね。これはjQueryなどを使っても同じことが言えます。この場合、UIを制御するために注視する必要があるのはDOMです。
Vueを使ったら
では、上記を簡単にVueの単一ファイルコンポーネントで表現してみましょう。本筋に関係ない細かいところは省きます。
<template> <div> <button @click="onClick">click me</button> <div v-if="isLoading">loading</div> <ul v-else> <li v-for="syain in syainList"> {{ syain }} </li> </ul> </div> </template> <script> export default { data() { return { syainList: [], isLoading: false, } }, methods: { async onClick() { this.isLoading = true; const list = await getSyainList(); this.syainList = list; this.isLoading = false; }, getSyainList() { return new Promise((res) => { setTimeout(() => res(['hige', 't-poyo', 'eririn']), 5000); }) } } }; </script>
細かい部分の説明は省略しますが、一見してわかる通り、DOM要素に直接触れる(document.なんちゃらを使う)ようなことはしていません。
一つ前の例では、DOM要素を直接削除することでloadingの切り替えを行っていましたが、Vueでは代わりにDataを操作していることがわかります。 マークアップ(template)部分については、あくまでDataに対してどのような表示を行うかの宣言のみが行われていることがわかるかと思います。 これを、宣言的UIの構築と言います。この場合UIを制御するために注視しなければならないのはDataです。
これができることによって、私たちフロントエンドエンジニアはマークアップする際に現在・過去のDOMの状態を知っておく必要があることから解放されました。しかしこれは、マークアップから状態が切り離されたとは言えど、あくまで切り離されただけでその複雑さは消滅してはいません。関心がDOMの状態からDataの状態へと移り変わったということです。
Vueでできることは色々とあります。
- コンポーネントシステム
- UIを部品に分けて実装・再利用できる仕組み
- リアクティブシステム
- Dataが更新されたらUIも更新されるし、UI側での変更がDataに反映される。 etc...
しかし、まず覚えておきたいのは上記の「宣言的にUIの構築ができる」という点です。
Vuexの話
なんとなくVueのことがわかってきたでしょうか?
SPAとは?CSR・SSRって何?という話や、パフォーマンスの話、設計手法の話……まだまだやりたいことはありますが、今回はVueの話はここまでにして、次にVuexの話をしましょう。
Vuexって何者?
端的に言ってしまうと、状態を管理するためのライブラリです。
Vueの話の際に説明しましたが、昨今のフロントエンド実装者が頭を悩ませている複雑性はDataの移り変わりにあります。 このDataの移り変わりを制御・管理することを指して「状態管理」と呼んでいるわけです。
Vueでは、コンポーネント(UIの部品)単位で状態を管理する仕組みが存在していますが、これらが相互に作用する場合や複数のコンポーネントが同一の状態に依存している場合、ことが一気に複雑化します。それら複雑な状態の交通整備をするのがこのVuexです。
Vuexの仕組み
簡単にVuexの仕組みについてお話しします。
まず、登場人物は以下の4名です。
- VueComponet
- UIの部品でUIを宣言している部分
- Action
- APIを叩いたりする部分
- Mutation
- Stateの更新を行う部分
- State
- 状態
下3つのAction,Mutation,StateがVuexで実装する部分です。
VueComponentは、Stateから受け取った状態をUIとして表現する部分。Actionは、VueComponentから発行(dispatch)され、API等を実行(副作用)し値をとってきたりする部分。Mutationは、Actionから実行され、Stateを更新する部分。Stateは、Mutationから更新され、Stateが更新された際VueComponentが再レンダリングされます。データの流れがぐるっと一周しているのがわかりますかね?逆流したりはしません。この一方通行のデータフローはFluxに触発された考え方です。(Fluxの話はまた今度)
図で表現すると、このような形になります。
それぞれの役割と何が嬉しいのかを、Stateから上に向かって説明していきます。
State
ここでは、アプリケーションの状態を一手に引き受け溜め込んでいます。そうすることで何が嬉しいのでしょうか?簡単に嬉しいケースをあげると、例えば、本来同一の状態を参照して表示しているはずのコンポーネントがそれぞれ状態をローカルに持ってしまっている場合。この場合、それぞれの状態に更新をかける必要がありますが、実装漏れを起こしてしまい、表示の整合性が崩れてしまったりしがちです。こういう時に、単一のStateを参照していればそのようなことは起こり得ないということですね。
Mutation
ActionとStateの間にある部分です。ここでは、Stateの変更を一手に管理しています。つまり、Stateの整合性を保っている部分ですね。嬉しい点としては、ここでStateの変更を一括管理することによってStateの更新ロジックがバラけるのを防ぐことができますし、このMutationの遷移を追いかけることで現在の状態を知ることができます。実際、Vuexの開発者ツールではそれを実行できます。
Action
これがなかなかの曲者です。何故これがMutationと別れているのか?という点に引っかかる初学者の方もいると思います。Mutationは同期的な関数でなければ、その遷移を追いかけて現在の状態を知ることができないため、非同期処理を扱うことはできません。なので、非同期を伴う処理はこのActionで行うことになります。(そもそもMutationの責務はStateの変更なので、非同期処理を伴うことはないはずですが)
Vuexの仕組み上、外界(BackendAPIとか)との境界になる部分はここになります。つまり、外界とStateの間にある緩衝材のような扱われ方をするわけですね。Actionは、必要に応じてAPI等から値を取得し、Stateに入れるのに相応しい形にしてMutationを呼び出すわけです。
なお、この辺りの作り方については色々な設計手法があるようなので、これもまた次の機会に。
Vuexって必須なの?
Vueを扱う上で状態管理については必須である、というような話をしてきましたが、Vuexは必須ではありません。 ここまでVuexを使って嬉しいことを挙げて来ましたが、実のところ嬉しくないところも結構あります。
- いくら交通整備しようとVuexのStateはグローバルな変数であること
- VuexのStateを参照する時には単純な文字列を使って参照しているので、実装時ツールなどで追いにくい(typoしたり)
- 純粋に実装する量が増える
「Vuex不要論」などで検索してもらえれば、ツヨツヨな先達の記事が出てくるはずなので是非そちらも参照してください。何も考えずにVueと同時に導入するのではなく、メリット・デメリットを天秤にかけて採用しましょう。いつも技術選定でやっていることですね。
具体的な設計手法
フロントエンドの状態管理について、Vuexは一定の指針を与えてくれますが、Moduleはどう切って、Actionの命名はこうで………というような具体的な手法についてはそれこそいろいろありますし、ただ一つの正解はないと思っています。
ひとつひとつのプロダクトのフェーズにおいて良い考え方は異なるため、しっかりとチームで議論し同意をとった規約を作ることが重要です。
まとめ
今回は、具体的な話にはあまり踏み入らずにフロントエンドのお話をしましたが、フロントを専門領域としない方にも興味を持ってもらえると幸いです。
自分自身まだまだ勉強中ですが、フロントエンドは移り変わりが激しく追いかけるのが楽しい領域です。 最近だと、WebpackのModuleFederationやReactのISR等、目新しいものも出て来ていますので、興味がある方はぜひhigeまで!一緒に勉強しましょう!
みんなの感想
- 素人の僕でも理解できた。初心者向けの情報から体系立てて説明してもらえてわかりやすかった。
- 過去の経緯も踏まえ、どうして状態管理が必要なのか納得しやすかった。(宣言的UI良いよね→でも状態管理大変だよね、みたいな流れ)
- インタラクティブに質問しながら聞けたので、理解が深まった。
- Vuexに限らずフロントエンドの色々な技術に興味が深まり、自分でも勉強してみようと思った。
- これまでJSを使っていたときは、DOMを直接操作していたが、それを行わなくてよいというのはとても便利になっているんだなと実感できた。
おわりに
estieではともにプロダクトを作り上げる仲間を募集しています! 少しでも興味を持たれた方がいらっしゃれば、気軽にお話しできればと思います。TwitterのDMよりお声がけください! twitter.com
採用情報はこちらをご確認ください!