iOS
vue.js
reactjs
reactnative
vuenative

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

はじめに

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

HelloWorldの記事もいいね数多め。:さっそくVue NativeでHello Worldしてみた - Qiita

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

作り方

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

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

まずはプロジェクト作成

Installation — Vue Native

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

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

アプリ起動

yarn start

するとQRコードとともにこんな結果がでてきます。
image.png

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

clipboard.png

補足

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

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

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

色を変えてみよう

App.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

コンポーネント作成

Todoの入力フォームと、そのTodoをリストに追加するボタンを追加していきます。
src/TodoInput.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で書くとこうなります。

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をforkして実装していることから、ReactNativeのコンポーネント名をキャメルケース(CamelCace)からケバブケース(kebab-case)に置き換えて使うイメージで大丈夫です。

Introducing Vue Native – GeekyAnts Blog

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

スタイル

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

ReactNativeではこうで

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

VueNativeではこう書けます

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

. . .

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

font-size: large;

適用されません。

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

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

コンポーネントの読み込み

App.vueTodoInput.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

コンソール出力

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

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

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

  export default {
    components: {
      TextInput
-    }
-  }
+    },
+    methods: {
+      _onPress : (text) => {
+        console.log(text);
+      },
+    }
+  }
  • App.vueで関数を定義
  • その関数_onPressを、on-add(onAdd)としてTextInputに渡す

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

-        <text-input class="text-input" />
-        <touchable-opacity class="button">
+        <text-input class="text-input" v-model="newText" />
+        <touchable-opacity class="button" :on-press="addTodo">
<script>
    export default {
+        props: {
+            onAdd: Function
+        },
+        methods: {
+          addTodo: function() {
+            this.onAdd(this.newText)
+          },
+        }
    }
</script>

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

すると
clipboard.png

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

一旦はここまでです。

ソースはgithubにあげてますので、わからなければこちらからどうぞ!