ReactにするかVue.jsにするか、jQueryだけ触っていたエンジニアがサンプル作って比較してみた


今のタイミングでWebサービスを、何か新しく開発しよーって思ったら、フロントサイドをどうしようか悩みますよね？
特にVue.jsかReact.jsか... :thinking: 
そんな悩んでいるあなたへの記事になります。

**自分の仕事領域について**
![pie-chart-2.jpg](https://qiita-image-store.s3.amazonaws.com/0/6808/62033e9a-c1ed-e791-65bb-6d34441a90fe.jpeg)

あまり普段javascript触ってません
触るとしてもjQueryが多いです
そんな人が書いた記事だと思って下さい

**今回作ったもの**
ReactとVue.jsで簡単なカレンダーを作成しました。

React
https://github.com/ykyk1218/react-calendar-sample

Vue.js
https://github.com/ykyk1218/vue-calendar-sample

<img width="383" alt="33513722-f2d72938-d78b-11e7-898e-7d770e0bfc8f.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/75895419-f941-570b-bc78-e83e7f64f08a.png">


**コンポーネントの分け方**
![無題のプレゼンテーション.jpg](https://qiita-image-store.s3.amazonaws.com/0/6808/3b559342-a031-c836-4563-06e15396ebc1.jpeg)

**今回言わないこと**

- テストについて
- ルーティングについて
- Reduxとかvuex

---


## 環境構築

### はじめに

Reactは`jsx`、Vue.jsは`.vue`という特殊な状態のファイルがあるので、それぞれシンタックスハイライトを設定できるプラグインとか入れとくと良いです。
僕はvimmerなんでvim用のプラグインいれときました。

・vue用
[vim-vue](https://github.com/posva/vim-vue)

・jsx用
[vim-javascript](https://github.com/pangloss/vim-javascript)
[vim-jsx](https://github.com/mxw/vim-jsx)


### React
create-react-appというreact環境が簡単に用意できるものをfacebookが用意しているので、それを使います。

ざっくり説明

1. `npm install -g create-react-app`
2. `create-react-app react-calendar-sample`
3. `cd react-calendar-sample`
4. `npm start`

http://localhost:3000/
へアクセス

詳しくはこちら！
https://reactjs.org/blog/2016/07/22/create-apps-with-no-configuration.html

### Vue.js

vue-cliというコマンドラインツールがあるので、それで作ります。

ざっくり説明
1. `npm install -g vue-cli`
2. `vue init webpack vue-calendar-sample`
3. `cd vue-calendar-sample`
4. `npm install` （時間かかる）
5. `npm run dev`

http://localhost:8080
へアクセス

詳しくはこちら！
https://github.com/vuejs/vue-cli

### 所感
どちらもコマンドで簡単にモダンな環境ができるので、楽です。
特にハマることもありませんでした。
実際のプロジェクトに組み込むとかなるとまた話は別かもしれませんが...

## CSSの書き方

どちらもコンポーネント指向なので、CSSもできれば閉じた中で使いたいですよね。

### React

コンポーネント内にCSSのscopeを収めるには`react-css-module`を使います。
create-react-appでやるには一手間必要。

ざっくり説明
1: npm run eject
2: npm install --save react-css-modules
3: config/webpack.config.dev.jsを編集


```js
// before
{

  loader: require.resolve('css-loader'),
  options: {
    importLoaders: 1,
  },
},

// after
{
  loader: require.resolve('css-loader'),
  options: {
    importLoaders: 1,
    modules: true,
    localIdentName: "[name]__[local]___[hash:base64:5]"
  },
},
```

4: コンポーネントでCSSModuleを呼ぶ

```js
import CSSModules from 'react-css-modules'
import styles from './css/Calendar.css' ←自分で作ったCSS
```

5: エクスポート時に指定

```js
export default CSSModules(Calendar, styles);
```

注意
CSSModuleの中のクラスを指定する場合は`className`ではなく、`styleName`で指定します。

```html
<div styleName='sample'>
```

こうすると普通のclassっぽく扱えてコンポーネント内だけに適用される閉じたscopeになります。

詳しくはこちら！（英語）
https://medium.com/nulogy/how-to-use-css-modules-with-create-react-app-9e44bec2b5c2


### Vue.js

意識することはほとんどない
.vueファイルで

```html
<style scoped>
.sample {
  font-size: 2em;
}
</style>
```
と言う感じで`scope`属性をつけるとコンポーネントだけに適用されるCSSになります。
便利。

### 所感
CSSはVue.jsの方がコンポーネントだけに適用させるCSSは書きやすいですね。 :smiley: 
とはいえReactもそれほど手順が面倒ではないので、大きな問題ではないと思います。

`react css`とかで検索すると、CSS in JSの話が出てくるのですが、あれはどうにもしっくりきません... :frowning2:

後一応の注意点としては、React x CSSModuleでは`.sample`はscopeが閉じられますが、`table`とかで指定するとグローバルになってしまいます。

## HTMLテンプレート

キモいキモい言われるjsxは、どうなのよ

今回は拡張的なものは入れませんでした。
pugと言われるテンプレートエンジンがありまして、それはReactでもVue.jsでもどちらも使うことができるようです。

とはいえReact x pugの事例はほとんどありませんでした...

React
https://github.com/pugjs/babel-plugin-transform-react-pug

Vue.js
https://qiita.com/shuuhei/items/4852210d362d2e9022d7


### React

カレンダーコンポーネントのhtmlを抜粋してます

```html

render() {
   let weekHtml = []
   this.week.forEach((v)=>{
     weekHtml.push(<td style={styles.calendarCell}>{v}</td>)
   })

   return (
     <div styleName='calendar'>
       <h2>{this.state.year}年{this.state.month}月</h2>
       <Button fullDate={this.state.fullDate} updateCalendarDate={this.updateCalendarDate}/>
       <div className="date"></div>
       <table styleName="calendar--tbl">
         <tbody>
           <tr>
             {weekHtml}
           </tr>
           <tr>
             {this.renderBlankTable(1)}
           </tr>
           <tr>
             {this.renderBlankTable(2)}
           </tr>
           <tr>
             {this.renderBlankTable(3)}
           </tr>
           <tr>
             {this.renderBlankTable(4)}
           </tr>
           <tr>
             {this.renderBlankTable(5)}
           </tr>
           <tr>
             {this.renderBlankTable(6)}
           </tr>
         </tbody>
       </table>
     </div>
   );
}
```


### Vue.js

```html
<template>
  <div id="calendar">
    <p>{{ year }}年{{ month }}月</p>
    <Button :fullDate="fullDate"/>
    <table>
      <tr>
        <td v-for="w in week">{{ w }}</td>
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 1)" />
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 2)" />
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 3)" />
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 4)" />
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 5)" />
      </tr>
      <tr>
        <DateCell v-for="(w, i) in week" :viewDate="viewDate(i, 6)" />
      </tr>
    </table>
  </div>
</template>

```

### 所感

Vue.jsの方がtemplate部分が綺麗に切り出せているので、書いていて気持ちがいい。Reactは普通にjavascriptでループ処理を書いてあげないといけないので、ロジックが入ってくるとどうしてもごちゃっとしてしまう気がします。

Reactはこうですが...

```html
let weekHtml = []
this.week.forEach((v)=>{
  weekHtml.push(<td style={styles.calendarCell}>{v}</td>)
})
```

Vue.jsはこう

```html
<td v-for="w in week">{{ w }}</td>
```


とはいえ...
Vue.jsは綺麗に書ける反面、処理が複雑になってくると逆に辛くなりそうです。
Reactの方はなんか、なんでもできそうな感じがあります。


## コンポーネント間の値受け渡し

ボタンコンポーネントでボタンをクリックした時に表示をどうやって切り替えているのかを見ていきましょう。


### React

**親→子**
コンポーネント呼び出し時に、属性として渡したい値を`key=val`の形で渡してあげます。

**子→親**
親のメソッドをpropsに渡して、callback的に子コンポーネントから呼び出すことで親に値を渡せます。

Reactのコンポーネントの値受け渡しは、こちらの記事がイメージつけやすかったです。
https://qiita.com/kyoshidajp/items/0ddb156d96bb6337f623

Calendar.js
カレンダーコンポーネントからボタンコンポーネントを呼び出す

```html

<Button fullDate={this.state.fullDate} updateCalendarDate={this.updateCalendarDate}/>

```

Button.js
ボタンコンポーネント側でpropsで渡ってきた、親のメソッドを呼ぶ

```html
  handleChangeCalendarPage = (event) => {
    const page = event.currentTarget.getAttribute("data-page")
    const fullDate = this.props.fullDate
    const tmpMonth = fullDate.getMonth() + 1 + parseInt(page)

    fullDate.setMonth(tmpMonth-1)

    const year = fullDate.getFullYear()
    const month =fullDate.getMonth()+1

    //call parent component method
    this.props.updateCalendarDate(year, month)
  }


  return(
    <div styleName="button-wrapper">
      <button styleName="button" style={styles.buttonLeft} onClick={this.handleChangeCalendarPage} data-page="-1">&lt; 前</button>
      <button styleName="button" style={styles.buttonRight} onClick={this.handleChangeCalendarPage} data-page="1">次 &gt;</button>
    </div>
  )
```

### Vue.js

**親→子**
親から子供への値の渡し方はReactとほぼ一緒です。
コンポーネントを定義時に属性として値を渡してあげます。

**子→親**
~~`this.$parent.hogehoge`で親コンポーネントの値にアクセスできます~~

$parentで親の値を見に行くのはアンチパターンのようでした。 :bow:
https://twitter.com/kazu_pon/status/789644939260928000



Calendar.vue

```html

<Button :fullDate="fullDate"/>

```


Button.vue

```html
<template>
  <div class="button-wrapper">
    <button class="button-left" v-on:click="handleCalendar('-1', fullDate)">&lt; 前</button>
    <button class="button-right" v-on:click="handleCalendar('1', fullDate)">次 &gt;</button>
  </div>
</template>
```

```js
<script>
  export default {
    name: 'Button',
    props: ['fullDate'],
    methods: {
      handleCalendar: function(page, fullDate) {
        const tmpMonth = fullDate.getMonth() + 1 + parseInt(page)
        fullDate.setMonth(tmpMonth-1)
        this.$parent.year  = fullDate.getFullYear()
        this.$parent.month = fullDate.getMonth()+1
        this.$parent.date = 0
      }
    }
  }
</script>
```

この辺で親コンポーネントのプロパティを直接見にいって値を変更してます。
Reactではこういうのできなさそうでした。
（探し方悪いだけかも...）

```js
this.$parent.year  = fullDate.getFullYear()
this.$parent.month = fullDate.getMonth()+1
this.$parent.date = 0
```

### 所感

ほぼ一緒。
子コンポーネントから親コンポーネントの値を変更するときに、Vueでは直接親の値を見にいけたので、一手間楽でした。
（でもこういうのはあんまりやらない方がいいのか...この辺の感覚知はまだない...）


## ハマったところ

知識不足が多分にあるところですが、一応

### React

**最後にexportし忘れていて、なんかimportできない！？なんで？**
エラーメッセージわかりづらいのですよ...
（そもそもexportさせないといけない、みたいな概念がなかったけど...）

**イベントハンドリングで引数渡すやり方**
ハマったってほどでもなかったけど、なんかうまいやり方ないかなーって探し求めて、data属性に値として渡すようにしました。

```html
<button styleName="button" style={styles.buttonLeft} onClick={this.handleChangeCalendarPage} data-page="-1">&lt; 前</button>
<button styleName="button" style={styles.buttonRight} onClick={this.handleChangeCalendarPage} data-page="1">次 &gt;</button>
```

### Vue.js

**メソッド内でdataの値を更新して、無限ループに陥る**

そりゃそーだろっていう話ですが :sweat_smile:
カレンダーコンポーネント内でループさせるたびにdateの値を1足していきたかったので下記のようにさせました

```js

data: function() {
  return {
    date: 1,
    week: ["日","月","火","水","木","金","土"],
　　 year: year,
    month: month,
    viewDate: function() {

      //============
      //色々な処理
      //============

      this.date += 1
      return this.date
    }
  }
}
```

dateが更新されたタイミングでviewDate関数がまた呼ばれてしまうので無限ループに陥ってしまいます。親切なことにVue.jsはブラウザが固まらないように、ループ処理を途中で打ち切ってくれます。

一応consoleにはエラー出ていたのでですが、なんでかなーと悩んでいました。 

**arrow関数で定義をした場合とfunction()で定義した場合にthisの値が異なる**

まだ理由がはっきりしていないのですが、arrow関数でthisを呼ぶとthisの値が期待したものでなくなります。
なんかノリで「やっぱ今はarrow関数っしょ」とかやるとundefinedが帰ってきて痛い目を見ます。

こちらに参考にして見てください。
[Vue.jsに書いてある「アロー関数は、this が期待する Vue インスタンスではなく・・・」とは？ - Qiita](https://qiita.com/_Keitaro_/items/d48733a19c10889e2365)


### 所感

全体通してVue.jsの方がハマった時間が多かったです。

わりかし最初に覚えておかないといけない記法とかがあるので、そこがスッと出てくるともっとスムーズにいったかなという印象でした。

## 全体まとめ

今のところの結論としては

- シンプルな処理はVue.jsの方が綺麗にかけて気持ちがいい
- 複雑なことやろうと思うとReactの方が柔軟にできそう
- Vueの方がハマる時間が多そう

というところでした。

後Vue.jsは検索すると、日本語の公式ドキュメントが上位にきてくれるので助かります。
Reactは公式ドキュメントを日本語に翻訳をしてくれている方が何人かいますが、ボランティア？でやっているので、メンテナンス性は微妙かも（ちゃんと読めてないのでわからないです）

React→Vue.jsに移行した人の記事はいくつかあるのですが、みんなReactに疲れた人が書いているのかVue.jsよりの記事が多いです。


ありがちな結論ではなりますが

- シンプルなWebアプリ→Vue.jp
- 複雑なWebアプリ→React

というのが今のところだとよさそうですね。

さてどっちを採用しようかな...

## おまけ：数値からみる比較

### Googleトレンド
<img width="649" alt="スクリーンショット 2017-12-03 23.04.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/82a4d4ef-4ec3-a6fc-6c19-999d5bae81dc.png">


青い線がVue.js、赤い線がReact
Vue.jsの方がトレンドとしてはキテます。


### Twitterフォロワー数

vue.js: 約5万3千
https://twitter.com/vuejs

react: 約19万
https://twitter.com/reactjs

圧倒的React

### Qiita投稿数
[vue.js](https://qiita.com/tags/vue.js) 713件
[React](https://qiita.com/tags/reactjs)：1290件

雰囲気そのうちVue.jsが抜きそうです。