フロントエンド開発: Webアプリケーションをコンポーネント化した話 - with Svelte

# なぜコンポーネント化しようと思ったのか

同じデザインのボタンがサイト上にたくさん出てくるのにもかかわらず、
それぞれのボタンに長いcssを割り当てていた。つまり冗長化しちゃってた、
(tailwindのあるある？)

```html
<button class="ml-1 shadow bg-yellow-500 hover:bg-yellow-400 focus:shadow-outline focus:outline-none text-white font-bold rounded">
  ボタン①
</button>

<button class="ml-1 shadow bg-yellow-500 hover:bg-yellow-400 focus:shadow-outline focus:outline-none text-white font-bold rounded">
  ボタン②
</button>

<button class="ml-1 shadow bg-yellow-500 hover:bg-yellow-400 focus:shadow-outline focus:outline-none text-white font-bold rounded">
  ボタン③
</button>
```

そのためにデザインを変えたいときに全てのボタンで変える必要があり、とても手間だった。

### 共通部品化したい！ => コンポーネント化してみよう！


# 開発の流れ

1. とりあえず一つのページの部品(コンポーネント)を洗い出す
1. それぞれのファイルを作成する
1. 呼び出して使えるようにする
1. 他のぺージでも使える部分へ適用する
1. 他のページの部品(コンポーネント)を洗い出す
1. 2へ戻る

(本来であれば画面設計の段階で考えるべきだったと後悔...)


# 実際に部品化してみる！

## 1. とりあえず一つのページの部品(コンポーネント)を洗い出す

* ログイン画面をよく見てみる
* 見えてきた！部品だ！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/d4c082ab-27e7-0a50-b2fa-89e0ad945bfd.png)

ということでメールアドレス用フォーム、パスワード用、Submit用ボタンを作成することに

## 2. それぞれのファイルを作成する

```
(いろいろ省略)
- lib
   |- Login.svelte
   |- Parts
        |- Buttons
        |    |- Submit.svelte
        |- Forms
             |- Email.svelte
             |- Password.svelte
```

## 3. 呼び出して使えるようにする

ここで気が付く。
あれ、、ボタンデザインは使いまわしたいけど、テキストは毎回違うな...

ということでテキストやプレースホルダーは呼び出すときに渡して変数としよう！

### Submit用ボタン

```vue
// Submit.svelte
<script lang="ts">
  export let text: string = '決定！';
</script>

<button
  class="w-full h-full shadow mx-auto bg-yellow-500 hover:bg-yellow-400 focus:shadow-outline focus:outline-none hover:border-yellow-400 text-white font-bold py-2 px-4 rounded"
  type="submit">{text}
</button>
```

```vue
// Login.svelte
<script lang="ts">
  import SubmitButton from './Parts/Buttons/Submit.svelte'
</script>

<SubmitButton text='ログイン' />
```

`export let`で定義することで親から渡された変数を受け取れるんですね～！
便利ですね！初期値は`決定！`にしました。


### あれ？<br>typeも変数にすればわざわざtypeごとにコンポーネントを分ける必要もないんじゃないか？<br><br>⇒ <font color="red">ダメでした。</font>typeは変数にしてはいけないそうです。

---

### EmailForm

emailのフォームは変数を渡すだけではなく、入力された値を親コンポーネントに渡す必要もあるので、`dispatch`を使って実現！
プレースホルダーも場合によっては親コンポーネントから受け取ってもいいかもしれません👍

```vue
// Email.svelte
<script lang="ts">
  import { createEventDispatcher } from 'svelte';
  export let value = '';

  const dispatch = createEventDispatcher();
  $: dispatch('updateChild', { value: value });
</script>

<input
  class="bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-purple-500"
  id="inline-full-name"
  placeholder="emailを入力..."
  name='email'
  type='email'
  required
  bind:value="{value}"
/>
```

```vue
<script lang="ts">
  import EmailForm from "../Parts/EmailForm.svelte"
  let email: string = '';

  const updateValue = (key, value) => {
    key = event.detail.value
  }
</script>

<EmailForm
  value={email}
  on:updateChild={(event) => {updateValue(email, event.detail.value)}}
/>
```

passwordのフォームも同じように`type=password`で同じように作りました！


## 4. 他のぺージでも使える部分へ適用する

パスワードを忘れた場合のメール送信画面の部品も洗い出したところ、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/8b9a3b0a-e882-f0b1-3736-25434b145a87.png)

先ほど作ったやつそのまま適用できるじやないか！
コードが半分になりました！


こんな感じでコンポーネント化をしたお話しでした。