Slide 1

Slide 1 text

©MNTSQ, Ltd. 2024/11/01 ReactからVueへの転向: 思考の変化とアプローチの違い 森山 凪

Slide 2

Slide 2 text

©MNTSQ, Ltd. 2 自己紹介

Slide 3

Slide 3 text

©MNTSQ, Ltd. 3 3年 1年 3ヶ月(趣味) 森山 凪 モリヤマ ナギ MNTSQ株式会社 フロントエンド 4年目

Slide 4

Slide 4 text

©MNTSQ, Ltd. 4 過去やってきたこと

Slide 5

Slide 5 text

©MNTSQ, Ltd. 5 前職はHR領域のSaaSをマイクロサービスアーキテクチャで開発 自己紹介 New HR SaaS Fix

Slide 6

Slide 6 text

©MNTSQ, Ltd. 6 新サービスの開発方針 自己紹介 GraphQLからTypeScriptの型を自動生成 Storybook x ChromaticでのUIの差分検知 Gherkin記法でユースケースをGit管理 Cypress x CucumberでE2E API、UI、ユースケース 変われば悲鳴をあげるFE

Slide 7

Slide 7 text

©MNTSQ, Ltd. 7 マイクロサービスそれぞれを見てみると、、、 自己紹介 HR SaaS あれ、、 各サービスで微妙にデザインに違うな

Slide 8

Slide 8 text

©MNTSQ, Ltd. 8 自己紹介 HR SaaS HR SaaS Private npm package UIコンポーネントライブラリの作成

Slide 9

Slide 9 text

©MNTSQ, Ltd. 9 ReactからVueへの転向: 思考の変化とアプローチの違い

Slide 10

Slide 10 text

©MNTSQ, Ltd. 10 目次 1. ライフサイクル表現の変化 2. 構文の変化 3. まとめ

Slide 11

Slide 11 text

©MNTSQ, Ltd. 11 目次 1. ライフサイクル表現の変化 2. 構文の変化 3. まとめ

Slide 12

Slide 12 text

©MNTSQ, Ltd. 12 ライフサイクル表現の変化

Slide 13

Slide 13 text

©MNTSQ, Ltd. 13 ライフサイクルの変化 React Vue useEffect lifecycle hook

Slide 14

Slide 14 text

©MNTSQ, Ltd. 14 ライフサイクルの変化 React Vue useEffect lifecycle hook

Slide 15

Slide 15 text

©MNTSQ, Ltd. 15 ライフサイクルの変化 React Vue useEffect() 1. update 2. mount 3. unMount =

Slide 16

Slide 16 text

©MNTSQ, Ltd. 16 ライフサイクルの変化 useEffect() update mount unMount

Slide 17

Slide 17 text

©MNTSQ, Ltd. 17 どうやってuseEffectで 3つのlifecycleを表現しているのか?

Slide 18

Slide 18 text

©MNTSQ, Ltd. 18 ライフサイクルの変化 useEffectのlifecycle表現 const Component = () => { useEffect( ()=>{ }, [ ] ) } 1. onMount 2. onUpdate 3. onUnmount

Slide 19

Slide 19 text

©MNTSQ, Ltd. 19 ライフサイクルの変化 const Component = () => { useEffect(()=>{ // 1回目はonMount },[]) } 1. onMount ○ Component()実行でuseEffect()も呼ばれる。 ○ useEffectの初回実行がmount 2. onUpdate 3. onUnmount useEffectのlifecycle表現

Slide 20

Slide 20 text

©MNTSQ, Ltd. 20 ライフサイクルの変化 1. onMount ○ Component()実行でuseEffect()も呼ばれる。 ○ useEffectの初回実行がmount。 2. onUpdate ○ useEffectの第2引数が変化する度に useEffectが再発火する。 ○ state1, state2どちらが変わっても発火。 3. onUnmount const Component = () => { const [state1,] = useState() const [state2,] = useState() useEffect(()=>{ // 1回目はonMount // 2回目以降はonUpdate },[state1, state2]) // 依存配列 } useEffectのlifecycle表現

Slide 21

Slide 21 text

©MNTSQ, Ltd. 21 ライフサイクルの変化 1. onMount ○ Component()実行でuseEffect()も呼ばれる。 ○ useEffectの初回実行がmount。 2. onUpdate ○ useEffectの第2引数が変化する度に useEffectが再発火する。 ○ state1, state2どちらが変わっても発火。 3. onUnmount ○ useEffectの第1引数の返り値がunmount。 const Component = () => { const [state1,] = useState() const [state2,] = useState() useEffect(()=>{ // 1回目はonMount // 2回目以降はonUpdate return () => {} // onUnmount },[state1, state2]) } useEffectのlifecycle表現

Slide 22

Slide 22 text

©MNTSQ, Ltd. 22 ライフサイクルの変化 なるほど!!

Slide 23

Slide 23 text

©MNTSQ, Ltd. 23 ライフサイクルの変化 useEffectはムズい

Slide 24

Slide 24 text

©MNTSQ, Ltd. 24 ライフサイクルの変化 useEffectムズい lifecycleを理解している人でないと (useEffectは)難しい

Slide 25

Slide 25 text

©MNTSQ, Ltd. 25 useEffectのなんのため?

Slide 26

Slide 26 text

©MNTSQ, Ltd. 26 ライフサイクルの変化 useEffectの恩恵は何?

Slide 27

Slide 27 text

©MNTSQ, Ltd. 27 ライフサイクルの変化 useEffectの恩恵は何? → 機能的な凝集性が高い

Slide 28

Slide 28 text

©MNTSQ, Ltd. 28 ライフサイクルの変化 useEffectの恩恵は何? → 機能的な凝集性が高い const Component = () => { // API コール useEffect(()=> { const response = apiCall(payload) setResponse(response) }, [payload]) // タイマー処理 useEffect(()=>{ const timerId = setInterval(() => { // tick process }, 1000) return () => clearInterval(timerId) },[]) }

Slide 29

Slide 29 text

©MNTSQ, Ltd. 29 ライフサイクルの変化 useEffectの恩恵は何? → 機能的な凝集性が高い const Component = () => { // API コール useEffect(()=> { const response = apiCall(payload) setResponse(response) }, [payload]) // タイマー処理 useEffect(()=>{ const timerId = setInterval(() => { // tick process }, 1000) return () => clearInterval(timerId) },[]) }

Slide 30

Slide 30 text

©MNTSQ, Ltd. 30 ライフサイクルの変化 useEffectの恩恵は何? → 機能的な凝集性が高い ● APIコール ○ mount時にAPIコール ○ payloadのupdate時にAPIコール ● タイマー ○ mount時にタイマーをスタート ○ unMount時にタイマーをクリア const Component = () => { // API コール useEffect(()=> { const response = apiCall(payload) setResponse(response) }, [payload]) // タイマー処理 useEffect(()=>{ const timerId = setInterval(() => { // tick process }, 1000) return () => clearInterval(timerId) },[]) }

Slide 31

Slide 31 text

©MNTSQ, Ltd. 31 ライフサイクルの変化 useEffectの恩恵は何? → 機能的な凝集性が高い ● APIコール ○ mount時にAPIコール ○ payloadのupdate時にAPIコール ● タイマー ○ mount時にタイマーをスタート ○ unMount時にタイマーをクリア const Component = () => { // API コール useEffect(()=> { const response = apiCall(payload) setResponse(response) }, [payload]) // タイマー処理 useEffect(()=>{ const timerId = setInterval(() => { // tick process }, 1000) return () => clearInterval(timerId) },[]) } 機能的な凝集性が高い→再利用がしやすい

Slide 32

Slide 32 text

©MNTSQ, Ltd. 32 lifecycle hookは?

Slide 33

Slide 33 text

©MNTSQ, Ltd. 33 ライフサイクルの変化 lifecycle hookの恩恵は?

Slide 34

Slide 34 text

©MNTSQ, Ltd. 34 ライフサイクルの変化 lifecycle hookの恩恵は? → 時間的な凝集性が高い

Slide 35

Slide 35 text

©MNTSQ, Ltd. 35 ライフサイクルの変化 lifecycle hookの恩恵は? → 時間的な凝集性が高い export default { watch: { payload: { this.fetchData(); } // APIコール }, mounted() { this.fetchData(); // APIコール this.timerId = setInterval(this.tick, 1000); // タイマー処理 }, beforeUnmount() { clearInterval(this.timerId); // タイマー処理 } };

Slide 36

Slide 36 text

©MNTSQ, Ltd. 36 ライフサイクルの変化 lifecycle hookの恩恵は? → 時間的な凝集性が高い export default { watch: { payload: { this.fetchData(); } // APIコール }, mounted() { this.fetchData(); // APIコール this.timerId = setInterval(this.tick, 1000); // タイマー処理 }, beforeUnmount() { clearInterval(this.timerId); // タイマー処理 } };

Slide 37

Slide 37 text

©MNTSQ, Ltd. 37 ライフサイクルの変化 lifecycle hookの恩恵は? → 時間的な凝集性が高い export default { watch: { payload: { this.fetchData(); } // APIコール }, mounted() { this.fetchData(); // APIコール this.timerId = setInterval(this.tick, 1000); // タイマー処理 }, beforeUnmount() { clearInterval(this.timerId); // タイマー処理 } }; ● update ○ APIコール ● mount ○ APIコール ○ タイマースタート ● unMount ○ タイマーをクリア

Slide 38

Slide 38 text

©MNTSQ, Ltd. 38 ライフサイクルの変化 lifecycle hookの恩恵は? → 時間的な凝集性が高い export default { watch: { payload: { this.fetchData(); } // APIコール }, mounted() { this.fetchData(); // APIコール this.timerId = setInterval(this.tick, 1000); // タイマー処理 }, beforeUnmount() { clearInterval(this.timerId); // タイマー処理 } }; ● update ○ APIコール ● mount ○ APIコール ○ タイマースタート ● unMount ○ タイマーをクリア 時間軸で機能は分断される。 物理的なまとまりが無いので機能の関連性を見 つけるのは難しい。

Slide 39

Slide 39 text

©MNTSQ, Ltd. 39 useEffect と lifecycle比較

Slide 40

Slide 40 text

©MNTSQ, Ltd. 40 ライフサイクルの変化 useEffect ● update ○ APIコール ● mount ○ APIコール ○ タイマースタート ● unMount ○ タイマーをクリア 時間的な凝集性が高い。 いつ何が起きるか分かりやすい。 機能が時間で切られて散らばる。 ● APIコール ○ mount時にAPIコール ○ payloadのupdate時にAPIコール ● タイマー ○ mount時にタイマーをスタート ○ unMount時にタイマーをクリア 機能的な凝集性が高い。 機能で再利用がしやすい。 発火タイミングを読み解く必要がある。 lifecycle hook

Slide 41

Slide 41 text

©MNTSQ, Ltd. 41 実装のアプローチ

Slide 42

Slide 42 text

©MNTSQ, Ltd. 42 ライフサイクルの変化 Composableのlifecycle hookが◎ Composableで機能的な凝集性が高い lifecycle hook郡を定義できる。 機能的な凝集性の高さを維持しつつ、 内部で発火タイミングも明確化。 function useInterval() { let timerId onMounted(() => { timerId = setInterval(() => { // tick process }, 1000) }); onUnmounted(() => { clearInterval(timerId) }); }

Slide 43

Slide 43 text

©MNTSQ, Ltd. 43 ライフサイクルの変化 function useInterval() { let timerId onMounted(() => { timerId = setInterval(() => { // tick process }, 1000) }); onUnmounted(() => { clearInterval(timerId) }); } Composableで機能的な凝集性が高い lifecycle hook郡を定義できる。 機能的な凝集性の高さを維持しつつ、 内部で発火タイミングも明確化。 Composableのlifecycle hookが◎

Slide 44

Slide 44 text

©MNTSQ, Ltd. 44 ライフサイクルの変化 function useInterval() { let timerId onMounted(() => { timerId = setInterval(() => { // tick process }, 1000) }); onUnmounted(() => { clearInterval(timerId) }); } Composableで機能的な凝集性が高い lifecycle hook郡を定義できる。 機能的な凝集性の高さを維持しつつ、 内部で発火タイミングも明確化。 Composableのlifecycle hookが◎ いいとこどり!

Slide 45

Slide 45 text

©MNTSQ, Ltd. 45 目次 1. ライフサイクル表現の変化 2. 構文の変化 3. まとめ

Slide 46

Slide 46 text

©MNTSQ, Ltd. 46 構文の変化

Slide 47

Slide 47 text

©MNTSQ, Ltd. 47 構文の変化 React(JSX) Save Vue(テンプレート) Save

Slide 48

Slide 48 text

©MNTSQ, Ltd. 48 構文の変化 React(JSX) Save Vue(テンプレート) Save 一番大きな変化

Slide 49

Slide 49 text

©MNTSQ, Ltd. 49 構文の変化 React(JSX) Save Vue(テンプレート) Save Vueには詳しいと思うので、、、

Slide 50

Slide 50 text

©MNTSQ, Ltd. 50 構文の変化 ReactのJSXって??

Slide 51

Slide 51 text

©MNTSQ, Ltd. 51 構文の変化 ReactのJSXって??

Slide 52

Slide 52 text

©MNTSQ, Ltd. 52 構文の変化 ReactのJSXって?? JSX (悪口が聞こえたような...)

Slide 53

Slide 53 text

©MNTSQ, Ltd. 53 構文の変化 ReactのJSXって?? 気持ち悪い ≒ よく知らないから?

Slide 54

Slide 54 text

©MNTSQ, Ltd. 54 JSXとは何者なのか?

Slide 55

Slide 55 text

©MNTSQ, Ltd. 55 構文の変化 JSXはJavaScriptの構文拡張 ● テンプレート拡張じゃない ● JavaScriptとXMLの組み合わせ ● ECMAScript2015に対する構文拡張 x =

Slide 56

Slide 56 text

©MNTSQ, Ltd. 56 構文の変化 JSXはJavaScriptの構文拡張 ● テンプレート拡張じゃない ● JavaScriptとXMLの組み合わせ ● ECMAScript2015に対する構文拡張 x = なにがJavaScriptに拡張されたの?

Slide 57

Slide 57 text

©MNTSQ, Ltd. 57 構文の変化 JSXはJavaScriptの構文拡張 ● テンプレート拡張じゃない ● JavaScriptとXMLの組み合わせ ● ECMAScript2015に対する構文拡張 x = なにがJavaScriptに拡張されたの? JSX.Element型

Slide 58

Slide 58 text

©MNTSQ, Ltd. 58 構文の変化 JSXはJavaScriptの構文拡張 ● テンプレート拡張じゃない ● JavaScriptとXMLの組み合わせ ● ECMAScript2015に対する構文拡張 ● JSX.Element型が使える ● TypeScriptが言語サポート x = const str: string = "hello world" const Element: JSX.Element = hoge

Slide 59

Slide 59 text

©MNTSQ, Ltd. 59 なぜReactはJSXなのか?

Slide 60

Slide 60 text

©MNTSQ, Ltd. 60 構文の変化 なぜReactはJSXなのか? →コンポーネントという関心の分離にフォーカスするため

Slide 61

Slide 61 text

©MNTSQ, Ltd. 61 構文の変化 なぜReactはJSXなのか? →コンポーネントという関心の分離にフォーカスするため

Slide 62

Slide 62 text

©MNTSQ, Ltd. 62 構文の変化 なぜReactはJSXなのか? →コンポーネントという関心の分離にフォーカスするため 分離すべきはコンポーネント。

Slide 63

Slide 63 text

©MNTSQ, Ltd. 63 構文の変化 なぜReactはJSXなのか?
→コンポーネントという関心の分離にフォーカスするため 分離すべきはコンポーネント。

Slide 64

Slide 64 text

©MNTSQ, Ltd. 64 構文の変化 なぜReactはJSXなのか?
→コンポーネントという関心の分離にフォーカスするため 分離すべきはコンポーネント。 UI・ロジックではない。 技術の分離は関心の分離には寄与しない。

Slide 65

Slide 65 text

©MNTSQ, Ltd. 65 構文の変化 なぜReactはJSXなのか?
分離すべきはコンポーネント。 UI・ロジックではない。 技術の分離は関心の分離には寄与しない。 であれば一つの技術(JavaScript)の表現力を活かし コンポーネントの分離にフォーカスする。 JavaScriptドリブン →コンポーネントという関心の分離にフォーカスするため

Slide 66

Slide 66 text

©MNTSQ, Ltd. 66 構文の変化 なぜReactはJSXなのか? Reactチーム初期メンバー: Pete Hunt React rethinking best practice Building components, not templates A framework cannot know how to separate your concerns for you. テンプレートではなくコンポーネントをつくる。 フレームワークは開発者がどのように関心の分離をするのか 分からない。

Slide 67

Slide 67 text

©MNTSQ, Ltd. 67 ではVueは?

Slide 68

Slide 68 text

©MNTSQ, Ltd. 68 構文の変化 Vueもやりたいことは同じ なぜSFCなのか(why-sfc) 関心の分離がファイルタイプの分離と同じではない コンポーネント内では、そのテンプレート、ロジック、およびスタイルが本質的に 結合されており、それらを連結することで、実際にコンポーネントがよりまとまり、 保守しやすくなります。 公式ドキュメントより

Slide 69

Slide 69 text

©MNTSQ, Ltd. 69 構文の変化 Vueもやりたいことは同じ なぜSFCなのか(why-sfc) 関心の分離がファイルタイプの分離と同じではない コンポーネント内では、そのテンプレート、ロジック、およびスタイルが本質的に 結合されており、それらを連結することで、実際にコンポーネントがよりまとまり、 保守しやすくなります。 SFCという概念が独立性の高いコンポーネントを作るギブス 公式ドキュメントより

Slide 70

Slide 70 text

©MNTSQ, Ltd. 70 構文の違い≒思想の違い

Slide 71

Slide 71 text

©MNTSQ, Ltd. 71 構文の変化 Webアプリケーションの捉え方 React Vue {{ header }} {{ content }} {{ footer }} React React-pdf React Native アプリケーションのレンダリング先がブラウザ 動的なHTMLテンプレート

Slide 72

Slide 72 text

©MNTSQ, Ltd. 72 構文の変化 思考の切り替え React UI(JSX)を返す関数を書く // JSX {condition && Visible}
    {items.map(item =>
  • {item.name}
  • )}
Visible
  • {{ item.name }}
Vue ディレクティブ等で動的なHTMLを作る

Slide 73

Slide 73 text

©MNTSQ, Ltd. 73 目次 1. 構文の変化 2. ライフサイクル表現の変化 3. まとめ

Slide 74

Slide 74 text

©MNTSQ, Ltd. 74 まとめ

Slide 75

Slide 75 text

©MNTSQ, Ltd. 75 まとめ まとめ ● useEffectはムズい。 ● lifecycle hookは機能的な凝集を意識 ● ReactはJavaScriptドリブン ● Vueは動的HTML ○ (Vapor modeに期待)

Slide 76

Slide 76 text

©MNTSQ, Ltd. 76 ご清聴ありがとうございました。

Slide 77

Slide 77 text

No content