10分ちょっとでわかるVueコンポーネントの書き方 for Reactエンジニア

# はじめに

この記事は、[Makuake Product Team Advent Calendar 2018](https://qiita.com/advent-calendar/2018/makuake) 16日目の記事です。

はじめまして。今年11月に株式会社マクアケにジョインした@ryosuke-morishigeです。
まだ、ジョインから２ヶ月未満ですが、初アドベントカレンダーや新オフィス移転(こちらは後日紹介記事があると思いますのでお楽しみに！！)などイベント目白押しで良い意味で目が回っております。

Makuakeでは、現在UIライブラリとして主に`Vue.js`を採用しております。以前は`React`しか扱ったことのなかった私は開発参加にあたって`Vue.js`を学ぶ必要がありました。

というわけで、今回はReactエンジニアに送る10分ちょっとで(大体)わかるVueコンポーネントというお題目で、半ば自分の備忘録も兼ねて書かせていただきます。

## 想定読者

- Reactはそこそこ書ける・理解している人
- 最近Vue流行り始めたし気になっている人
- 初歩的なところは飛ばしてさっと概要だけ掴みたい人

## 書かないこと

- 環境構築周り：とりあえず触りたいならvue-cli入れればいいと思います
- Flux(Vuex)周り：話が長くなるので
- 単体テスト周り：現場次第ですがReactエンジニア的にはJest + vue-test-utilsでいいと思います

# 本題

VueとReactを比較しつつ、簡単に要点のみ紹介していきます。Reactの記法については様々あると思いますが一例ということでご容赦ください。

## 単一ファイルコンポーネント

```vue
<template>
  <div class="greeting">
    {{greeting}}
  </div>
</template>

<script>
export default {
  name: 'Greeting',
  props: {
    greeting: {
      type: String,
      required: true,
    }
  }
};
</script>

<style scoped>
.greeting {
    color: blue;
}
</style>
```

Vueには単一ファイルコンポーネント以外の簡単な方法も用意されていますが、Reactを書いたことがあるならばこちらの方が馴染みにやすいと思います。単一ファイルコンポーネントとは`.vue`で示されるファイルの中に`template`と`script`、`style(css, scssなど)`をひとまとめに定義したものです。ReactだとStyleは置いておくとして、`.jsx`なファイルの中に書いてきたものをイメージするとよいです。

## template

HTML準拠なテンプレートです。いくつかVue独自のパラメータや要素が存在し、アクションの割当や表示の条件分岐を行っていきます。

### 文字展開

```vue
<template>
    <div>{{ msg }}</div>
</template>
```

二重中括弧で指定します。括弧内には`<script>`に定義されている`data`, `props`, `computed`などの値が指定できます。特に指定しない限りは値が変更されれば自動的に再レンダリングされます。(これは文字展開だけに限らず、バインディングされた値が変更されると再レンダリングが走ります。)

### if

- Vue
```vue
<template>
    <div v-if="isShow">Hello</div>
</template>
```

Reactだと

```jsx
{isShow && <div>Hello</div>}
```

```jsx
const hello = isShow ? <div>Hello</div> : null;
render() {
    return hello;
}
```

### if-else

```vue
<template>
    <div v-if="isBlue">Blue</div>
    <div v-else>Red</div>
</template>
```

Reactだと

```jsx
{isBlue ? <div>Blue</div> : <div>Red</div>}
```

```jsx
const color = isBlue ? <div>Blue</div> : <div>Red</div>;
render() {
    return color;
}
```

### for-in

```vue
<template>
    <template v-for="item in items">
        <div key="item.id">{item.name}</div>
    </template>
</template>
```
※ `<template>`はブラウザ上には反映されない要素です。

Reactだと

```jsx
{items.map((item) => (<div key={item.id}>{item.name}</div>))}
```

### コンポーネント呼び出し

```vue
<template>
  <div>
    <awesome-component />
  </div>
</template>

<script>
import AwesomeComponent from '@/AwesomeComponent';

default export {
    name: 'Root',
    components: {
        AwesomeComponent
    }
}
</script>
```

コンポーネントファイルをimportして、コンポーネント名を独自タグとして呼び出すところはReactと同じですね。Vueの場合は`<script>`内で`components`に指定してあげる必要があります。

`<template>`内で呼び出す場合は、若干面食らいますがコンポーネント名をケバブケース(`kebab-case`)にして記述するのがスタンダードです。(Reactのようにキャメルケースで呼び出すことも可能です。)

### アクション指定

```vue
<template>
    <button v-on:click="handleClick">Click me</button>

    <!-- 略記法 -->
    <button @click="handleClick">Click me</button>
</template>

<script>
default export {
    name: 'Root',
    methods: {
        handleClick(e) {
            e.preventDefault();
            console.log('click');
        }
    }
}
</scirpt>
```

Reactだと

```jsx
handleClick = (e) => {
    e.preventDefault();
    console.log
}

render() {
    return (
        <button onClick={this.handleClick}>Click me</button>
    );
}
```

### slot

子コンポーネントをwrapするようなコンポーネントに使用します。

```vue
<template>
  <div class="something-wrapper">
    <slot />
  </div>
</template>
```

Reactだと

```jsx
render() {
    return <div>{children}</div>;
}
```


## script

メソッドや呼び出すコンポーネントなどを記述する箇所です。


### name

コンポーネントの名称を指定します。独自タグ等として扱われます。基本的にUpperCamelCase。

```vue
<script>
default export {
    name: 'ComponentName'
}
</script>
```

Reactだと

```jsx
class ComponentName extends React.Component {
    /* 略 */
}

// or

const ComponentName = (props) => {
    /* 略 */
}
```

### props

Reactの同名のものと同じ役割を持ちます。基本的にlowerCamelCase。

```vue
<template>
    <div v-if="isShow">{{message}}</div>
</template>

<script>
  export default {
      name: 'ComponentName',
      props: {
          message: {
              type: String,
              required: true
          },
          isShow: {
              type: Boolean,
              default() {
                  return true;
              }
          }
      }
  }
</script>
```

typeやvalidationなどは必ずしも指定する必要はありません。(指定した方がコンソールで警告してくれるので安全ですが)

```vue
<script>
  export default {
      name: 'ComponentName',
      props: ['message', 'isShow']
  }
</script>
```

### data

Reactの`state`に近い役割を持ちます。`state`とは違い、`setState()`を用いなくても変更時に再レンダリングが走ります。

```vue
<template>
    <div @click="incriment">カウント：{{count}}</div>
</template>

<script>
 export default {
    name: 'ComponentName',
    data() {
        return {
            count: 0
        }
    },
    methods: {
        increment() {
            this.count = this.count + 1;
        }
    }
 }
</script>
```

### computed

React（の標準）では類似のない機能です。算出関数を書いてあげると、依存する値が変更されたときに自動的に検出して再計算してくれます。

```vue
<template>
    <div @click="incriment">カウント：{{count}}</div>
    <div>２倍：{{doubleCount}}</div>
</template>

<script>
 export default {
    name: 'ComponentName',
    data() {
        return {
            count: 0
        }
    },
    computed: {
        doubleCount() {
            return this.count * 2;
        }
    }
    methods: {
        increment() {
            this.count = this.count + 1;
        }
    }
 }
</script>
```

### methods

`computed`、`filter`にあたらない関数を実装するための箇所です。ちなみにアロー関数の使用は非推奨されています。

## style

Vueは標準でコンポーネント内でCSSなどを用いたスタイル定義を行えます。通常のCSSと変わりなく記述できます。

```vue
<template>
    <div class="blue">Hello</div>
</template>
<style>
    .blue {
        color: blue;
    }
</style>
```

### scoped

styleの適用範囲を自コンポーネントのみに抑えます。他コンポーネントのclass名を気にせず書けるようになるので非常に便利です。

```vue
<template>
    <div class="blue">Hello</div>
</template>

// 他のコンポーネントには適用されない！！
<style scoped>
    .blue {
        color: blue;
    }
</style>
```

# 最後に

ReactとVue.jsは同じUIライブラリですが、コンポーネントの書き方やAPIを並べると思想の違いの輪郭が浮かび上がってくるような気がします。

粗はあると思いますが、自分と同じVueの初学者の方に役立てていただければ幸いです！

また、株式会社マクアケは各種エンジニアを募集しております。興味を持っていただけた方がおられましたら、ぜひ「[各種エンジニア募集！ クラウドファンディングMakuake](https://www.wantedly.com/projects/26807)」からご連絡ください。

残すところ後２週間ですが、「[Makuake Product Team Advent Calendar 2018](https://qiita.com/advent-calendar/2018/makuake)」は毎日更新で続きます！明日もお楽しみに！