How to make front-end / How to make front-end

E37b4344ef4bfd0fc4826c04971e54fb?s=47 nrs
August 31, 2019

How to make front-end / How to make front-end

builderscon 2019 にて発表したスライドです。
フロントエンドなどの GUI プログラミングにおけるシンプルなコードを達成するためのセオリーについてお話しています。
補足解説: https://nrslib.com/how-to-make-front-end/

# URL
HomePage: https://nrslib.com
Twitter: https://twitter.com/nrslib

E37b4344ef4bfd0fc4826c04971e54fb?s=128

nrs

August 31, 2019
Tweet

Transcript

  1. How to make front-end Masanobu Naruse

  2. 2

  3. 3

  4. シンプルさを支える基本の考えは ベクトル 4

  5. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 5
  6. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 6
  7. MVC MVP MVVM 7

  8. MVC 8

  9. Complex 9

  10. 10

  11. Simple 11

  12. 12

  13. 13

  14. In software 14

  15. Classic MVC 15

  16. Model View Controller Classic MVC 16

  17. Model View Controller Classic MVC 17

  18. Model View Controller Classic MVC 18

  19. Model View Controller Classic MVC 19

  20. Model View Controller Classic MVC 20

  21. Model View Controller Classic MVC 21

  22. Model View Controller Classic MVC GUI Pattern 22

  23. Model View Controller Classic MVC 23

  24. Model View Controller Classic MVC Observer 24

  25. Model View Controller Classic MVC Observer 25

  26. Model View Controller Classic MVC Observer 26

  27. MVC Framework ? 27

  28. Model View Controller MVC2 28

  29. MVP 29

  30. MVP Humble View Supervising Controller 30

  31. MVP Humble View Supervising Controller 31

  32. Model View MVP (Humble View) Presenter 32

  33. Model View MVP (Humble View) Presenter 33

  34. Model View MVP (Humble View) Presenter 34

  35. Model View MVP (Humble View) Presenter 35

  36. Model View MVP (Humble View) Presenter 36

  37. Model View MVP (Humble View) Presenter 37

  38. Model View MVP (Humble View) Presenter 38

  39. MVP Humble View Supervising Controller 39

  40. Model View MVP (Super Vising Controller) Presenter 40

  41. Model View MVP (Super Vising Controller) Presenter 41

  42. Model View MVP (Super Vising Controller) Presenter 42

  43. Model View MVP (Super Vising Controller) Presenter 43

  44. Model View MVP (Super Vising Controller) Presenter 44

  45. Model View MVP (Super Vising Controller) Presenter 45

  46. Presenter Model View MVP (Super Vising Controller) 46

  47. Model View MVP (Super Vising Controller) Observer Presenter 47

  48. Model View MVP (Super Vising Controller) Presenter Observer 48

  49. 49

  50. MVC は MVP になった 50

  51. MVVM 51

  52. Model View MVVM ViewModel 52

  53. Model View MVVM ViewModel 53

  54. Model View MVVM Binding ViewModel 54

  55. Model View MVVM Binding ViewModel 55

  56. Model View MVVM Binding ViewModel 56

  57. Model View MVVM Binding ViewModel 57

  58. MVC MVP MVVM 58

  59. MVC MVP MVVM 59

  60. 重要なのは Model と View を分けること 糊付け方法が C, P, VM MVC

    MVP MVVM 60
  61. というわけで 独自アーキテクチャ 61

  62. MVX 62

  63. MVX Model View Xxx 63

  64. MVX Model View Xxx を提唱してたら 64

  65. MVW 65

  66. MVW Model View Whatever 66

  67. Flux ? 67

  68. 68

  69. 69

  70. Classic MVC は MVP となり その実装案のひとつとして Flux 70

  71. 重要なのは データフロー 71

  72. 重要なのは データフロー 72

  73. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 73
  74. 74

  75. Id: 1 Name: naruse Twitter: @nrslib 75

  76. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 76
  77. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 77
  78. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal 78
  79. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs 79
  80. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Modal Name: nrs 80
  81. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 81
  82. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Update Name: nrs 82
  83. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 83
  84. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 84
  85. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib どこが悪いか もう一度 85
  86. 86

  87. Id: 1 Name: naruse Twitter: @nrslib 87

  88. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 88
  89. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 89
  90. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 90
  91. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal 91
  92. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs 92
  93. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Modal Name: nrs 93
  94. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 94
  95. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 95
  96. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 一時的な データ不整合 96
  97. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 一時的な データ不整合 たとえるなら 97
  98. 98

  99. どうすればよいか 99

  100. 100

  101. Id: 1 Name: naruse Twitter: @nrslib 101

  102. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 102
  103. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 103
  104. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal 104
  105. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs 105
  106. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs Update Name: nrs 106
  107. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs 107
  108. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib Modal Name: nrs Id: 1 Name: nrs Twitter: @nrslib 108
  109. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Modal Name: nrs 109
  110. Modal 110

  111. Model View Controller Classic MVC 111

  112. 112

  113. フォーム? 113

  114. 114

  115. Id: 1 Name: naruse Twitter: @nrslib 115

  116. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 116
  117. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 117
  118. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: naruse

    Twitter: @nrslib 118
  119. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib 119
  120. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Update Name: nrs 120
  121. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Id: 1 Name: nrs Twitter: @nrslib 121
  122. Id: 1 Name: nrs Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Id: 1 Name: nrs Twitter: @nrslib あくまでサーバのデータで フロントを更新する 122
  123. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Update Name: nrs 123
  124. Id: 1 Name: naruse Twitter: @nrslib Id: 1 Name: nrs

    Twitter: @nrslib Update Name: nrs 124
  125. Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs

    Twitter: @nrslib Update Name: nrs Id: 1 Name: naruse Twitter: @nrs 125
  126. Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs

    Twitter: @nrslib Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs Twitter: @nrs 126
  127. Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs

    Twitter: @nrs Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs Twitter: @nrs 127
  128. Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs

    Twitter: @nrs Id: 1 Name: naruse Twitter: @nrs Id: 1 Name: nrs Twitter: @nrs 信頼たるデータを 信頼しよう 128
  129. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 129
  130. エラー 130

  131. 好きですか? エラー 131

  132. エラーハンドリング 132

  133. 好きですか? エラーハンドリング 133

  134. エラーハンドリングは ソフトウェアの使い勝手を 決定づける重要な要素 134

  135. その上でもう一度 135

  136. 好きですか? エラーハンドリング 136

  137. 僕はあんまり好きじゃないです 137

  138. 本質的じゃないから 138

  139. どうすれば? 139

  140. エラーを整理してみよう 140

  141. ユーザが回復できるエラー システムエラー 141

  142. ユーザが回復できるエラー システムエラー 拾うべきは? 142

  143. ユーザが回復できるエラー システムエラー 143

  144. 更にエラーを分ける 144

  145. クライアントエラー サーバーエラー 145

  146. クライアントの システムエラー クライアントの 回復可能エラー サーバーの システムエラー サーバーの 回復可能エラー サ ー

    バ ク ラ イ ア ン ト システムエラー 回復可能エラー 146
  147. クライアントの システムエラー クライアントの 回復可能エラー サーバーの システムエラー サーバーの 回復可能エラー サ ー

    バ ク ラ イ ア ン ト システムエラー 回復可能エラー 147
  148. クライアントの システムエラー クライアントの 回復可能エラー サーバーの システムエラー サーバーの 回復可能エラー サ ー

    バ ク ラ イ ア ン ト システムエラー 回復可能エラー 148
  149. nrs@@example.com メールアドレス 149

  150. nrs@@example.com メールアドレス メールアドレスの形式 ではありません 150

  151. ******** パスワード ******** パスワードの確認 151

  152. ******** パスワード ******** パスワードの確認 パスワードが 一致しません 152

  153. 153

  154. 画面ごとに異なるハンドリングのため それぞれの画面でハンドリング 154

  155. 画面ごとに異なるハンドリングのため それぞれの画面でハンドリング ディレクティブなどで共通化 155

  156. クライアントの システムエラー クライアントの 回復可能エラー サーバーの システムエラー サーバーの 回復可能エラー サ ー

    バ ク ラ イ ア ン ト システムエラー 回復可能エラー 156
  157. naruse ユーザ名 naruse@example.com メールアドレス ******** パスワード ******** パスワードの確認 157

  158. naruse ユーザ名 naruse@example.com メールアドレス ******** パスワード ******** パスワードの確認 すでに登録されています すでに登録されています

    158
  159. 159

  160. Mail: naruse@example.com Name: naruse Password: ******** 160

  161. Mail: naruse@example.com Name: naruse Password: ******** duplicated-email, duplicated-username 161

  162. Mail: naruse@example.com Name: naruse Password: ******** duplicated-email, duplicated-username duplicated-email だからメール欄に

    エラー表示して 162
  163. Mail: naruse@example.com Name: naruse Password: ******** duplicated-email, duplicated-username duplicated-email だからメール欄に

    エラー表示して duplicated-username だからユーザ名欄に エラー表示して 163
  164. duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して 164

  165. duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して 画面ごとに ハンドリング? 165

  166. 166

  167. 167

  168. 登録済みのメールアドレスです このユーザ名はすでに取得されています naruse ユーザ名 naruse@example.com メールアドレス ******** パスワード ******** パスワードの確認

    168
  169. 登録済みのメールアドレスです このユーザ名はすでに取得されています naruse ユーザ名 naruse@example.com メールアドレス ******** パスワード ******** パスワードの確認

    これなら? 169
  170. 170

  171. duplicated-email だからメール欄に エラー表示して 171

  172. duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して 172

  173. そのほかにも duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して 173

  174. そのほかにも duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して アレやコレや 174

  175. エラーコードに従って エラーメッセージを表示 そのほかにも duplicated-email だからメール欄に エラー表示して duplicated-username だからユーザ名欄に エラー表示して アレやコレや

    175
  176. どう実現する? 176

  177. 通信ライブラリを ラップする 177

  178. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 178
  179. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 179
  180. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 180
  181. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 181
  182. const notifyErrorToMessageService: IErrorNotifier = (response: IResponse) => { response.errors .map(x

    => x.message) .forEach(message => messageService.addMessage(MessageType.Error, message) ); }; 182
  183. const notifyErrorToMessageService: IErrorNotifier = (response: IResponse) => { response.errors .map(x

    => x.message) .forEach(message => messageService.addMessage(MessageType.Error, message) ); }; 183
  184. <template> <div v-if="hasAnyMessage"> {{head}} <template v-for="tailMessage in tail"> <br> {{tailMessage}}

    </template> </div> </template> ... @Component export default class NotifyMessage extends Vue { ... public get hasAnyMessage(): boolean { return this.messageService.hasAnyMessage; } public get head(): string { return this.messageService.headMessage; } public get tail(): string[] { return this.messageService.tailMessages; } } 184
  185. <template> <div v-if="hasAnyMessage"> {{head}} <template v-for="tailMessage in tail"> <br> {{tailMessage}}

    </template> </div> </template> ... @Component export default class NotifyMessage extends Vue { ... public get hasAnyMessage(): boolean { return this.messageService.hasAnyMessage; } public get head(): string { return this.messageService.headMessage; } public get tail(): string[] { return this.messageService.tailMessages; } } 185
  186. 細かく制御したい ときもある 186

  187. 入力画面の難易度が高い エラー発生個所がわかりづらい たとえば 187

  188. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 188
  189. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 189
  190. export default class Apios { public static post<T>(url: string, data?:

    any, params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ): Promise<ApiosResponse<T>> { const onError = this._selectErrorNote(params.onError); ... return new Promise<ApiosResponse<T>>((resolve, reject) => { Axios.post(url, data, { headers: { token: token } }) .then((res) => { const response = this._convertErrorMessage(res.data); if (this.isIResponse(response)) { if (response.errors.length > 0) { onError(response); } } const apiosResponse = new ApiosResponse(false, response); resolve(apiosResponse); }) .catch((e: AxiosError) => { unexpectedErrorHandler<T>(resolve, reject, e, onError); }); }); 呼び出し側がエラーハンドラを 設定できるように受け口を用意 190
  191. Callback を渡すようになるので 必然的に Promise を使わなくなる 191

  192. Callback を渡すようになるので 必然的に Promise を使わなくなる 192

  193. Promise 地獄 193

  194. Promise を使えるようにするより 場合分けをしっかりしたほうがいい 194

  195. できごと ハンドリング 成功 失敗 例外 セッション切れ 権限変更 (パーミッション変更) 195

  196. できごと ハンドリング 成功 Callback 失敗 例外 セッション切れ 権限変更 (パーミッション変更) 196

  197. できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 セッション切れ

    権限変更 (パーミッション変更) 197
  198. できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない

    or Callback セッション切れ 権限変更 (パーミッション変更) 198
  199. できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない

    or Callback セッション切れ ログイン画面へ 権限変更 (パーミッション変更) 199
  200. できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない

    or Callback セッション切れ ログイン画面へ 権限変更 (パーミッション変更) しない(権限エラー通知) 200
  201. できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない

    or Callback セッション切れ ログイン画面へ 権限変更 (パーミッション変更) しない(権限エラー通知) 201
  202. public static post<T>(url: string, data?: any, onSuccess: T => void,

    params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ) できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない or Callback セッション切れ ログイン画面へ 権限変更 (パーミッション変更) しない(権限エラー通知) 202
  203. public static post<T>(url: string, data?: any, onSuccess: T => void,

    params?: { onError?: IErrorNotifier; unexpectedErrorHandler?: Handler<T>; config?: AxiosRequestConfig; } ) できごと ハンドリング 成功 Callback 失敗 しない or Callback 例外 しない or Callback セッション切れ ログイン画面へ 権限変更 (パーミッション変更) しない(権限エラー通知) 203
  204. 基本は何もしなくてよくて 必要なときに差し込める というのが大事 204

  205. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 205
  206. コンポーネント構造のテーマ 1. コンポーネント構造 2. ページ遷移や通信を行う箇所 3. Flux 系のライブラリの使いどころ 4. データの保持は誰の役目?

    206
  207. コンポーネント構造のテーマ 1. コンポーネント構造 2. ページ遷移や通信を行う箇所 3. Flux 系のライブラリの使いどころ 4. データの保持は誰の役目?

    207
  208. まずはいわゆる オブジェクト指向プログラミング 208

  209. シンプルな オブジェクト同士の構造は? 209

  210. ObjectA ObjectB 210

  211. ObjectA ObjectB 211

  212. ObjectA ObjectB Method Call 212

  213. ObjectA ObjectB Method Call Method Call 213

  214. ObjectA ObjectB Method Call Method Call 214

  215. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 215
  216. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 216
  217. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 217
  218. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 相互参照 218
  219. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 相互参照 コンストラクタに this を渡してると 大抵ヤバイ 219
  220. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 220
  221. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 221
  222. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 222
  223. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 223
  224. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } } class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { ... this.objectA.methodForB(); } 224
  225. ObjectA ObjectB 225

  226. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 226
  227. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 227
  228. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 228
  229. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 229
  230. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 230
  231. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 231
  232. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 232
  233. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 233
  234. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 234
  235. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 235
  236. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 236
  237. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 237
  238. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 238
  239. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 239
  240. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 240
  241. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 241
  242. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 242
  243. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 243
  244. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 244
  245. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 245
  246. class ObjectA { private objectB = new ObjectB(this); public method()

    { this.objectB.methodForA(); } public methodForB() { ... } public methodForB2() { ... this.objectB.methodForA2(); } public methodForB3() { this.objectB.methodForA3(); ... this.objectB.methodForA4(); } public methodForB4() { ... class ObjectB { public constructor( private readonly objectA: ObjectA) { } public method() { this.objectA.methodForB(); } public methodForA() { this.objectA.methodForB(); ... this.objectA.methodForB2(); } public methodForA2() { ... this.objectA.methodForB3(); } public methodForA3() { ... } public methodForA4() { 動作順序 把握できました? 246
  247. ObjectA ObjectB methodForA methodForB methodForB2 methodForA2 methodForB3 methodForA3 methodForA4 247

  248. ObjectA ObjectB methodForA methodForB methodForB2 methodForA2 methodForB3 methodForA3 methodForA4 把握できました?

    248
  249. どうするか? 249

  250. ObjectA ObjectB 250

  251. ObjectA ObjectB Parent ObjectA ObjectB 251

  252. class Parent { private readonly objectA = new ObjectA(); private

    readonly objectB = new ObjectB(); public method() { this.objectB.methodForA(); this.objectA.methodForB(); this.objectA.methodForB2(); this.objectB.methodForA2(); this.objectA.methodForB3(); this.objectB.methodForA3(); this.objectB.methodForA4(); } } 252
  253. class Parent { private readonly objectA = new ObjectA(); private

    readonly objectB = new ObjectB(); public method() { this.objectB.methodForA(); this.objectA.methodForB(); this.objectA.methodForB2(); this.objectB.methodForA2(); this.objectA.methodForB3(); this.objectB.methodForA3(); this.objectB.methodForA4(); } } class ObjectA { public methodForB() { ... } public methodForB2() { ... } public methodForB3() { ... } public methodForB4() { ... } } class ObjectB { public methodForA() { ... } public methodForA2() { ... } public methodForA3() { ... } public methodForA4() { ... } } 253
  254. class Parent { private readonly objectA = new ObjectA(); private

    readonly objectB = new ObjectB(); public method() { this.objectB.methodForA(); this.objectA.methodForB(); this.objectA.methodForB2(); this.objectB.methodForA2(); this.objectA.methodForB3(); this.objectB.methodForA3(); this.objectB.methodForA4(); } } class ObjectA { public methodForB() { ... } public methodForB2() { ... } public methodForB3() { ... } public methodForB4() { ... } } class ObjectB { public methodForA() { ... } public methodForA2() { ... } public methodForA3() { ... } public methodForA4() { ... } } オブジェクトは シンプルに 254
  255. class Parent { private readonly objectA = new ObjectA(); private

    readonly objectB = new ObjectB(); public method() { this.objectB.methodForA(); this.objectA.methodForB(); this.objectA.methodForB2(); this.objectB.methodForA2(); this.objectA.methodForB3(); this.objectB.methodForA3(); this.objectB.methodForA4(); } } 255
  256. class Parent { private readonly objectA = new ObjectA(); private

    readonly objectB = new ObjectB(); public method() { this.objectB.methodForA(); this.objectA.methodForB(); this.objectA.methodForB2(); this.objectB.methodForA2(); this.objectA.methodForB3(); this.objectB.methodForA3(); this.objectB.methodForA4(); } } コードを見れば 一目瞭然 256
  257. コンポーネントも 同じ 257

  258. TabButton TabButton TabButton TabButton 258

  259. TabButton TabButton TabButton TabButton Click 259

  260. TabButton TabButton TabButton TabButton Click Select 260

  261. TabButton TabButton TabButton TabButton Click Select 261

  262. TabButton TabButton TabButton TabButton Click unselect() Select 262

  263. TabButton TabButton TabButton TabButton Click unselect() Select 263

  264. TabButton TabButton TabButton TabButton Click unselect() Select 264

  265. TabButton TabButton TabButton TabButton TabButtonGroup 265

  266. TabButton TabButton TabButton TabButton TabButtonGroup Click 266

  267. TabButton TabButton TabButton TabButton TabButtonGroup Click Clicked 267

  268. TabButton TabButton TabButton TabButton TabButtonGroup Un Select Un Select Un

    Select Un Select 268
  269. TabButton TabButton TabButton TabButton TabButtonGroup Un Select Un Select Un

    Select Un Select 269
  270. TabButton TabButton TabButton TabButton TabButtonGroup Select 270

  271. TabButton TabButton TabButton TabButton TabButtonGroup Select 271

  272. TabButton TabButton TabButton TabButton TabContent Click 272

  273. TabButton TabButton TabButton TabButton TabContent Clicked TabButtonGroup 273

  274. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Clicked Clicked (B) Tab

    274
  275. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Un Select 275

  276. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Un Select Un

    Select Un Select Un Select Un Select 276
  277. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Un Select Un

    Select Un Select Un Select Un Select 277
  278. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) 278

  279. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) Select

    279
  280. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) Select

    280
  281. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Change Content 281

  282. TabButton TabContent TabButtonGroup Tab 282

  283. TabButton TabContent TabButtonGroup Tab 283

  284. TabButton TabContent TabButtonGroup Tab メソッド 284

  285. TabButton TabContent TabButtonGroup Tab メソッド 285

  286. TabButton TabContent TabButtonGroup Tab メソッド イベント 286

  287. TabButton TabContent TabButtonGroup Tab メソッド イベント コンポーネントの基本構造 287

  288. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Clicked Tab Clicked (B)

    288
  289. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Clicked Tab イベント Clicked

    (B) 289
  290. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) Select

    290
  291. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) Select

    メソッド 291
  292. TabButton TabButton TabButton TabButton TabContent TabButtonGroup Tab Select (B) Select

    メソッド Vue などでは これをバインディングで 実現する 292
  293. たとえば 293

  294. <template> <button @click="onClick"> <slot /> {{selected}} </button> </template> <script lang="ts">

    import {Component, Emit, Prop, Vue} from "vue-property-decorator"; @Component export default class TabButton extends Vue { @Prop() public selected!: boolean; @Emit('click') public onClick() { } } </script> 294
  295. <template> <button @click="onClick"> <slot /> {{selected}} </button> </template> <script lang="ts">

    import {Component, Emit, Prop, Vue} from "vue-property-decorator"; @Component export default class TabButton extends Vue { @Prop() public selected!: boolean; @Emit('click') public onClick() { } } </script> 295
  296. <template> <button @click="onClick"> <slot /> {{selected}} </button> </template> <script lang="ts">

    import {Component, Emit, Prop, Vue} from "vue-property-decorator"; @Component export default class TabButton extends Vue { @Prop() public selected!: boolean; @Emit('click') public onClick() { } } </script> 296
  297. <template> <ul> <TabButton v-for="param in parameters" :key="param.id" :selected="isSelected(param.id)" @click="onTabButtonClick(param.id)"> {{param.title}}

    </TabButton> </ul> </template> <script lang="ts"> ... export default class TabButtonGroup extends Vue { @Prop() public selectedTabId!: string; @Prop() public parameters!: ITabButtonParameter[]; @Emit('tabButtonClick') public onTabButtonClick(id: string) { } public isSelected(id: string) { return id === this.selectedTabId; } } 297
  298. <template> <ul> <TabButton v-for="param in parameters" :key="param.id" :selected="isSelected(param.id)" @click="onTabButtonClick(param.id)"> {{param.title}}

    </TabButton> </ul> </template> <script lang="ts"> ... export default class TabButtonGroup extends Vue { @Prop() public selectedTabId!: string; @Prop() public parameters!: ITabButtonParameter[]; @Emit('tabButtonClick') public onTabButtonClick(id: string) { } public isSelected(id: string) { return id === this.selectedTabId; } } 298
  299. <template> <ul> <TabButton v-for="param in parameters" :key="param.id" :selected="isSelected(param.id)" @click="onTabButtonClick(param.id)"> {{param.title}}

    </TabButton> </ul> </template> <script lang="ts"> ... export default class TabButtonGroup extends Vue { @Prop() public selectedTabId!: string; @Prop() public parameters!: ITabButtonParameter[]; @Emit('tabButtonClick') public onTabButtonClick(id: string) { } public isSelected(id: string) { return id === this.selectedTabId; } } 299
  300. <template> <div> <TabButtonGroup :selectedTabId="selectedTabId" :parameters="tabContents" @tabButtonClick="onTabGroupTabButtonClick" /> <slot :selectedTabId="selectedTabId" />

    </div> </template> <script lang="ts"> ... export default class Tab extends Vue { public tabContents: TabContent[] = []; public selectedTab: TabContent | null = null; @Prop() public selectedTabId!: string; public mounted() { const tabContents = this.$slots.default .filter(x => x.componentInstance) .map(x => x.componentInstance) .map(x => x as TabContent); this.tabContents = tabContents; this.onTabGroupTabButtonClick(this.tabContents[0].id); } @Emit('tabButtonClick') public onTabGroupTabButtonClick(id: string) { } 300
  301. <template> <div> <TabButtonGroup :selectedTabId="selectedTabId" :parameters="tabContents" @tabButtonClick="onTabGroupTabButtonClick" /> <slot :selectedTabId="selectedTabId" />

    </div> </template> <script lang="ts"> ... export default class Tab extends Vue { public tabContents: TabContent[] = []; public selectedTab: TabContent | null = null; @Prop() public selectedTabId!: string; public mounted() { const tabContents = this.$slots.default .filter(x => x.componentInstance) .map(x => x.componentInstance) .map(x => x as TabContent); this.tabContents = tabContents; this.onTabGroupTabButtonClick(this.tabContents[0].id); } @Emit('tabButtonClick') public onTabGroupTabButtonClick(id: string) { } 301
  302. <template> <div> <TabButtonGroup :selectedTabId="selectedTabId" :parameters="tabContents" @tabButtonClick="onTabGroupTabButtonClick" /> <slot :selectedTabId="selectedTabId" />

    </div> </template> <script lang="ts"> ... export default class Tab extends Vue { public tabContents: TabContent[] = []; public selectedTab: TabContent | null = null; @Prop() public selectedTabId!: string; public mounted() { const tabContents = this.$slots.default .filter(x => x.componentInstance) .map(x => x.componentInstance) .map(x => x as TabContent); this.tabContents = tabContents; this.onTabGroupTabButtonClick(this.tabContents[0].id); } @Emit('tabButtonClick') public onTabGroupTabButtonClick(id: string) { } 302
  303. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 303
  304. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 304
  305. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 305
  306. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 306
  307. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 307
  308. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 308
  309. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 子コンポーネント同士の 取り回しはあくまでも それを保持する親コンポーネントが 責任をもっておこなう 309
  310. <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <Tab :selectedTabId="selectedTabId" @tabButtonClick="onTabButtonClick">

    <TabContent title="first" :active="isActive('first')"> ContentsFirst </TabContent> <TabContent title="second" :active="isActive('second')"> ContentsSecond </TabContent> ... </Tab> </div> </template> <script lang="ts"> ... export default class Home extends Vue { public selectedTabId: string = "first"; public onTabButtonClick(id: string) { this.selectedTabId = id; } public isActive(id: string) { return id === this.selectedTabId; } } </script> 子コンポーネント同士が 勝手に反応しない ようにすること 310
  311. コンポーネント構造のテーマ 1. コンポーネント構造 2. ページ遷移や通信を行う箇所 3. Flux 系のライブラリの使いどころ 4. データの保持は誰の役目?

    311
  312. ページ遷移 312

  313. PageA PageB ComponentA Button 313

  314. PageA PageB ComponentA Button Click 314

  315. PageA PageB ComponentA Button Click 315

  316. PageA PageB ComponentA Button 316

  317. PageA PageB ComponentA Button 317

  318. PageA PageB ComponentA Button Click 318

  319. PageA PageB ComponentA Button Click 319

  320. PageA PageB ComponentA Button Click 320

  321. PageA PageB ComponentA Button 321

  322. PageA PageB ComponentA Button ページ遷移は ページコンポーネントを見れば 分かるようにすること 322

  323. PageA PageB Link Component これはページを見れば分かる (ページに遷移先情報がある) ので OK Parameter Link

    to PageB 323
  324. 通信 324

  325. Page Component Data 325

  326. Page Component Http Data 326

  327. Page Component Http Http Data 327

  328. Page Component Http Http コンポーネントの使いまわしがない限り 通信も基本はページ (データもページが保持してバインディング) Data 328

  329. コンポーネント構造のテーマ 1. コンポーネント構造 2. ページ遷移や通信を行う箇所 3. Flux 系のライブラリの使いどころ 4. データの保持は誰の役目?

    329
  330. Flux は 複数のコンポーネントを 制御する目的で 使うのは控える 330

  331. コンポーネントの基本構造に 従うことを優先する 331

  332. コンポーネント構造のテーマ 1. コンポーネント構造 2. ページ遷移や通信を行う箇所 3. Flux 系のライブラリの使いどころ 4. データはどうやって変更する?

    332
  333. Component Component Data Component 333

  334. Component Component Data Component Action 334

  335. Component Component Data Component Action Event 335

  336. Component Component Data Component Action Event Event 336

  337. Component Component Data Component Action Event Event 337

  338. Component Component Data Component Action Event Event データを変更するのは データを保持するコンポーネント 338

  339. モデルバインディングは? 339

  340. <input v-model="message"> <p>Message is: {{ message }}</p> 340

  341. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    341
  342. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    342
  343. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    実はこうではない 343
  344. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    344
  345. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    Event 345
  346. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    Event 346
  347. <input v-model="message"> <p>Message is: {{ message }}</p> Component Input message

    Event モデルバインディングは シンタックスシュガー 347
  348. Agenda 1. GUIアーキテクチャパターン 2. データ同期 3. エラーハンドリング 4. コンポーネント構造 5.

    まとめ 348
  349. シンプルなコードを 達成するための セオリー 349

  350. 350

  351. ベクトルを意識 351

  352. ベクトルを意識 データの流れ 実装の流れ 処理の流れ を一定にする 352

  353. GUI プログラミングで 目指すべきゴール 353

  354. Humble View 354

  355. 慎ましい View を 目指すこと 355

  356. テストする必要もないほど 慎ましい View を 目指すこと 356

  357. Auther Masanobu Naruse HomePage https://nrslib.com Twitter @nrslib