Vue NativeでTodoアプリを作ろう その１

## はじめに
VueNativeが話題です。公開してから数日でgithubスター数3000を超えました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="es" dir="ltr">Introducing Vue Native!<a href="https://t.co/vIjdcELvkV">https://t.co/vIjdcELvkV</a> <a href="https://t.co/2mfmOHNbuw">pic.twitter.com/2mfmOHNbuw</a></p>&mdash; Sanket Sahu (@sanketsahu) <a href="https://twitter.com/sanketsahu/status/1006541239854874624?ref_src=twsrc%5Etfw">2018年6月12日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

HelloWorldの記事もいいね数多め。：[さっそくVue NativeでHello Worldしてみた - Qiita](https://qiita.com/taquaki-satwo/items/523f65723cf2bf7b049a)

ということでVue Nativeを触ってみます。
ゴールはTodoアプリですが、この記事では一旦コンソール出力までを実装します。

## 作り方
今回はReactNativeアプリをクローンしてみます。以下の記事をVueNativeでやってみたです

* [【React Native入門】Java Scriptでアプリ作ろう【Reactやったことない人者向け】１](https://qiita.com/YutamaKotaro/items/dd7846c6db15e2307daa)
* [【React Native入門】Java Scriptでアプリ作ろう【Reactやったことない人向け】2](https://qiita.com/YutamaKotaro/items/0521d83049536e451f15)

VueNativeは中身的にはReactNativeの技術の上に乗っているので、エラー処理なんかはReactNativeの知識が求められそう。一度目を通しても良いかもしれません。


## まずはプロジェクト作成
[Installation — Vue Native](https://vue-native.io/docs/installation.html)

`Vue Native Cli`を使ってアプリを作ります。
これを使うために更に`create-react-native-app`が必要になるので、global installします。

```sh
yarn global add create-react-native-app
yarn global add vue-native-cli
vue-native init todoapp
cd todoapp/
```

## アプリ起動
```
yarn start
```
するとQRコードとともにこんな結果がでてきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/103475/1ec123c2-ef07-b9a4-112c-f6bf3f94b7de.png)

`i`でiosエミュレーターが起動します。

![clipboard.png](https://inkdrop-uploads.s3.amazonaws.com/attachments/user-7fda3cbf548c4b9657c023a882715379/file:HJXU2iXWQ/index-public)



### 補足
これはさっき導入した`create-react-native-app`によって提供されている開発環境でexpoとfacebookがコラボして作られたツールです。
そもそも、Vue Nativeは内部的にReact Nativeをラップして実装しているようで、`vue-native-cli`も`create-react-native-app`をラップして実装しているのでしょう。

こちらの`create-react-native-app`は、**xcode**や**Android Studio**を経由せず、**Expo**を使ってアプリが実行されます。
**xcode**や**Android Studio**のビルド環境が絡むと（特にAndroid）、地雷踏んだときにめっちゃ時間かかります。Expoを使ってみた感じ、アプリケーションの実装を考えるだけで済むため、開発者に有意義なツールです。
とはいえカスタマイズするとなると脱Expoをしないといけないので、導入時のタイミングにはありかもしれないです。

ちなみに、QRコードを使って手元のスマホアプリから確認するときは同じネットワーク(WiFi)に接続してくださいね。（この`192.168.160.30`はローカルIPアドレスなので）

## 色を変えてみよう
`App.vue`を以下の内容で編集してみます。

```vue:App.vue 
<template>
  <view class="container">
    <text class="welcome">{{message}}</text>
  </view>
</template>

<script>
  export default {
    data: function() {
      return {
        message: "Vue Nativeへようこそ"
      }
    }
  }
</script>

<style>
.container {
  background-color: #333;
  align-items: center;
  justify-content: center;
  flex: 1;
}
.welcome {
  color: white;
  font-size: 20px;
  margin: 20px;
}
</style>
```

![clipboard.png](https://inkdrop-uploads.s3.amazonaws.com/attachments/user-7fda3cbf548c4b9657c023a882715379/file:SJ0qjsXbm/index-public)


## コンポーネント作成
Todoの入力フォームと、そのTodoをリストに追加するボタンを追加していきます。
`src/TodoInput.vue`を作ります。

```vue:TodoInput.vue
<template>
    <view class="container">
        <text-input class="text-input" />
        <touchable-opacity class="button">
            <text class="button-text">追加</text>
        </touchable-opacity>
    </view>
</template>

<script>
    export default {}
</script>

<style>
.container {
    flex-direction: row;
    padding: 20px;
}
.text-input {
    background-color: white;
    margin-right: 5px;
    flex: 3;
}
.button {
    flex: 1;
    background-color: #008080;
    margin-left: 5px;
    align-items: center;
    justify-content: center;
    padding-top: 10px;
    padding-bottom: 10px;
}
.button-text {
    color: white;
    font-weight: normal;
}
</style>
```

ちなみにReactNativeで書くとこうなります。

```js:TodoInput@ReactNative
import React, { Component } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  TextInput,
  StyleSheet,
} from 'react-native';

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    padding: 20,
  },
  textInput: {
    flex: 3,
    backgroundColor: '#FFF',
    marginRight: 5,
  },
  button: {
    flex: 1,
    backgroundColor: '#008080',
    marginLeft: 5,
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: 10,
    paddingBottom: 10,
  },
  buttonText: {
    color: '#FFF',
    fontWeight: '500',
  }
});

export default class TodoInput extends Component {
  render() {
    return (
      <View style={styles.container}>
        <TextInput style={styles.textInput}/>
        <TouchableOpacity style={styles.button}>
          <Text style={styles.buttonText}>追加</Text>
        </TouchableOpacity>
      </View>
    );
  }
}
```

## コンポーネントについて
VueNativeはReactNativeのコンポーネントを使って実装します。
[react-vue](https://github.com/SmallComfort/react-vue)をforkして実装していることから、ReactNativeのコンポーネント名をキャメルケース(CamelCace)からケバブケース(kebab-case)に置き換えて使うイメージで大丈夫です。
>[Introducing Vue Native – GeekyAnts Blog](https://blog.geekyants.com/introducing-vue-native-b66f71d50438)

ここでは`<TouchableOpacity>`を`<touchable-opacity>`と書きます。

## スタイル
ReactNativeでは、StyleSheetを使い、CSSをjsのオブジェクトとして定義しています(CSS in JS)。つまり、純粋なCSSでは書けません。
Vue NativeではCSSで書けます。いいですね。

ReactNativeではこうで

```js:これが
  buttonText: {
    color: '#FFF',
    fontWeight: '500',
  }
```

VueNativeではこう書けます

```css:こう
.button-text {
    color: white;
    font-weight: normal;
}
```
. . .

ちなみに対応していないCSSもあるみたいです。

```css:
font-size: large;
```
適用されません。

VueのCSSから、ReactNativeのstyleにマッピングがうまくできてない感じがします。
（`react-vue`でマッピングができてない？）

buildエラーになるパターンと、そうならないパターンに遭遇しましたが、条件を追求できていないので分かり次第追記します。

## コンポーネントの読み込み
`App.vue`に`TodoInput.vue`を組み込んでみましょう。

```vue:
<template>
  <view class="container">
    <view class="main">
      <text-input />
    </view>
  </view>
</template>

<script>
  import TextInput from './src/TodoInput';
  export default {
    components: {
      TextInput
    }
  }
</script>

<style>
.container {
  background-color: #333;
  flex: 1;
  padding-top: 40px;
  align-items: center;
}
.main {
  flex: 1;
  max-width: 400px;
  align-items: center;
}
</style>
```
1. scriptタグで`TextInput`をインポート
2. scriptタグで`components`として`TextInput`を定義
3. ケバブケースでコンポーネントを記述: `<text-input />`

するとこんな感じで画面に表示されるようになります。

![clipboard.png](https://inkdrop-uploads.s3.amazonaws.com/attachments/user-7fda3cbf548c4b9657c023a882715379/file:rylb9iXWQ/index-public)


## コンソール出力
テキストを入力して追加ボタンを押すとconsoleに出力できるようにしてみましょう。

`App.vue`は以下の編集をします。

```diff
-      <text-input />
+      <text-input
+        :on-add='_onPress'
+      />

```
```diff

  export default {
    components: {
      TextInput
-    }
-  }
+    },
+    methods: {
+      _onPress : (text) => {
+        console.log(text);
+      },
+    }
+  }
```

* App.vueで関数を定義
* その関数`_onPress`を、`on-add`(onAdd)としてTextInputに渡す

つぎに、`TodoInput.vue`の編集です。

```diff
-        <text-input class="text-input" />
-        <touchable-opacity class="button">
+        <text-input class="text-input" v-model="newText" />
+        <touchable-opacity class="button" :on-press="addTodo">
```

```diff
<script>
    export default {
+        props: {
+            onAdd: Function
+        },
+        methods: {
+          addTodo: function() {
+            this.onAdd(this.newText)
+          },
+        }
    }
</script>
```

テキストを入力して追加ボタンを押してみてください。

すると
![clipboard.png](https://inkdrop-uploads.s3.amazonaws.com/attachments/user-7fda3cbf548c4b9657c023a882715379/file:Hkvau3Q-m/index-public)

ターミナルにconsole.logの内容が出力されます。
xcodeを使った場合だとchromeのコンソールに出力されますが、こちら（expo）はターミナルに出力される感じです。

一旦はここまでです。

ソースはgithubにあげてますので、わからなければ[こちら](https://github.com/nitaking/vue-native-todo/tree/bcf8bfec7deb63ff17075c2717274ccdc87eff87)からどうぞ！