guntamania

モバイルのプロトタイプをVue.jsで作る

[2018-12-13 Thu]

この記事はFUJITSU Advent Calendar 2018の13日目の記事です。

記事の内容は全て個人の見解であり、執筆内容は執筆者自身の責任です。 所属する組織は関係ありません。


モバイルのプロトタイピングをVueをつかってやってみようという話。 モバイル開発者にとってはJavascriptのフレームワークなんてのは あまり馴染みがないとは思うんだけど、 ネイティブアプリとWebアプリの垣根がずいぶんなくなってきてるわけだから、 せっかくなので、やってみよう。

demo

以下に動作デモがあります。

https://medaka.github.io/vue-project/dist/

ソースは以下。

https://github.com/medaka/vue-project

対象読者

プログラミングがある程度できるが、 Javascriptフレームワークはあまり使ったことないって人を想定しています。 そのため、環境構築のあたりはしつこめに説明したつもり。

メリット

などなどいきなりネイティブアプリでプロトタイピングするのに比べ、 メリットがあるわけです。

環境

chocolatey を使います。 これはWindowsのためのパッケージ管理ソフトです。 便利なので、導入していない人は是非導入しよう。

nodeの導入

まずはnodeを導入する必要がある。 chocolateyがあれば簡単! 以下で入ります。

PS C:\Users\me> choco install nodejs
 : 
Do you want to continue?([Y]es/[N]o): Y
パス 'C:\ProgramData\chocolatey\lib-bad' へのアクセスが拒否されました。

と出る場合は「管理者で実行」しよう

data/vue/admin.png

そしてPowerShellを 再起動 すると、=npm= コマンドが使用できる状態になっているはず。 以下を入力して、nodeコマンドが使えるようになっているか 確認しよう。

PS C:\Users\me> node --version
v10.13.0

npmの設定

nodeをインストールすると、 npm コマンドが使えるようになる。 これはJavascriptのためのパッケージ管理ツールだ。

そんなわけで、プロキシの設定を忘れずにやらないといけない。 以下のように設定すればプロキシは通る。 @ マークがユーザ名に入る場合は %40 </code>に書換えよう。

PS C:\Users\me> npm -g config set proxy "http://username:password@proxy.server.com:8080"
PS C:\Users\me> npm -g config set https-proxy "http://username:password@.proxy.server.com:8080"

Vueの導入

いよいよVueの導入となる。 以下のコマンドでvueをインストールしよう。

PS C:\Users\me> npm install -g @vue/cli
PS C:\Users\me> npm install -g @vue/cli-init

これで vue コマンドが使えるようになった。 以下のコマンドで vue コマンドが使えるか確認しよう。

PS C:\Users\me> vue --version
3.1.3

ここで環境変数 https_proxy にプロキシを設定。認証付きの場合は http://username:password@proxy.server.com を設定すること。

PS C:\Users\me> set HTTP_PROXY=http://username:password@proxy.server.com
PS C:\Users\me> set HTTPS_PROXY=http://username:password@proxy.server.com

vueプロジェクトの作成

以上でvueコマンドが使用可能となった。 ここで、プロジェクトを作成してみよう。

PS C:\Users\me> vue init webpack onsen-project

このコマンドで作成できるが、色々聞かれる。 今回は以下のように答える。

? Project name vue-project               # このまま
? Project short name: onsen              # 一応変更
? Project description A Vue.js project   # このまま
? Author me                              # このまま
? Vue build standalone                   # このまま (Compiler + run time)
? Install Vuex for state management? No  # Noを選択
? Use ESLint to lint your code?          # Noを選択
? Setup unit tests with Karma + Mocha?   # Noを選択
? Setup e2e tests with Nightwatch?       # Noを選択
? Should we run `npm install` for ...    # npmを選択

# Project initialization finished!
# ========================

To get started:

  cd vue-project
  npm run dev
  
Documentation can be found at https://vuejs-templates.github.io/webpack

これでテンプレートはできた。 この状態でも動作させることができる。 devモードで動かしてみよう。

PS C:\Users\me> cd onsen-project
PS C:\Users\me\onsen-project> npm run dev

色々と処理が走り、以下が出現する。

DONE  Compiled successfully in 12272ms

I  Your application is running here: http://localhost:8080

ブラウザで http://localhost:8080/ にアクセスしてみよう。 サンプルページが現れるはずだ。

Onsen UIの導入

今回はモバイル用プロトを作成するのが目的なので、 Onsen UIを導入する。

Onsen UIとは

Onsen UIはAndroidおよびiOSに対応した、ネイティブアプリのような UI・UXを提供してくれるCSSフレームワークである。 これによって、モバイルアプリっぽいものがWebで作れる。 しかもAndroidとiOSはアクセスする端末によって表示を切り替えられるので、 開発者は意識する必要がない。

vueアプリへ導入

導入するには、まずnpmコマンドをうつ。

PS C:\Users\me\onsen-project> npm install onsenui vue-onsenui --save-dev

そして以下のコードを src/main.js に貼り付ける。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'

+ // Webpack CSS import
+ import 'onsenui/css/onsenui.css';
+ import 'onsenui/css/onsen-css-components.css';
+ // JS import
+ import VueOnsen from 'vue-onsenui'; // This already imports 'onsenui'
+ Vue.use(VueOnsen);
import router from './router'

以上でvue.jsでOnsen UIの使用が可能となる。

プロトタイピングする

これで準備がととのった!いよいよプロトタイピングに入ろう。 ここでは例として、以下のような実装を行っていきたいと思う。 一応プロトタイピングでありそうな状況を想定している。

 

ドロワメニューの追加

App.vueを以下のように大改造する。 中身についてはおいおいわかっていけばいいと思う。。

<template>
  <div id="app">
    <v-ons-toolbar>
      <div class="left">
        <v-ons-toolbar-button @click="openSide = !openSide">
          <v-ons-icon icon="ion-navicon, material:md-menu"></v-ons-icon>
        </v-ons-toolbar-button>
      </div>
      <div class="center">vue-project</div>
    </v-ons-toolbar>
    <v-ons-splitter class="content">
      <v-ons-splitter-side
        swipeable width="150px" collapse="" side="left"
        :open.sync="openSide">
        <v-ons-page>
          <v-ons-list>
            <v-ons-list-item v-for="page in pages"
              tappable modifier="chevron"
              @click="openSide = false">
              {{page}}
            </v-ons-list-item>
          </v-ons-list>
        </v-ons-page>
      </v-ons-splitter-side>

      <v-ons-splitter-content>
        <router-view />
      </v-ons-splitter-content>
    </v-ons-splitter>
  </div>
</template>

<script>
export default {
  name: 'App',
  data: () => {
    return({
      openSide: false,
      pages: ["menu1", "menu2"]
    });
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.content {
  margin-top: 58px;
}
</style>

おいおいとはいえ、 簡単にvueファイルの説明をしないといけない。 聡明な諸兄にはあらかた想像できていると思うが、 vueファイルは以下のセクションから成る。

これがメタなxmlとなっており、それぞれ template がテンプレートhtmlの記述、 scriptがjavascriptの記述、 styleがcssの記述のためのセクションとなっている。

templateの箇所の改造内容はポイントはいくつかあって、 目的はドロワを出すことにある。 ドロワは <v-ons-splitter> に相当する。 また、 <router-view /> がvue-routerが出すコンテンツだ。 つまり、ここにページ本体が描画されることになる。

あとは少しややこしいので、現段階ではおまじないで..

この状態で以下を実行してみよう。

PS C:\Users\me\vue-project> npm run dev

この状態でブラウザにアクセスすると、 以下のようなスクリーンショットになる。

data/vue/first-drawer.png

上記はChromeのdevtoolでmobile表示にしている。

data/vue/devtool-mobile.png

ドロワを追加しただけなので、左のmenu1,menu2をクリックしても 何も起こらない。

ページ追加

このアプリにページを追加してみよう。

components/Page1.vue を新規作成し、以下をとりあえずを入力し、 保存する。

<template>
<div>
  <ons-page>
    aaa
  </ons-page>
</div>
</template>
<script>
export default {
  name: 'Page1',
  data () {
    return {
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>

</style>

aaa と表示するだけのページだ。 あとはvueのひな形が入力されている。

そして router/index.js に以下を追加する。 これにより、URLと先ほど追加したページが紐づけされるのだ。

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
+ import Page1 from '@/components/Page1'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
+   },
+   {
+     path: '/page1',
+     name: 'Page1',
+     component: Page1
    }
  ]
})

ドロワにメニューを追加するため、 App.vue を変更しよう。 ページを追加するほかには、ページ遷移のスクリプトを追加している。

    <v-ons-splitter class="content">
      <v-ons-splitter-side
        swipeable width="150px" collapse="" side="left"
        :open.sync="openSide">
        <v-ons-page>
          <v-ons-list>
            <v-ons-list-item v-for="page in pages"
              tappable modifier="chevron"
+             @click="openSide = false; $router.push(page.path)">
+             {{page.label}}
            </v-ons-list-item>
          </v-ons-list>
        </v-ons-page>
      </v-ons-splitter-side>

      <v-ons-splitter-content>
        <router-view />
      </v-ons-splitter-content>
    </v-ons-splitter>
        :
    data: () => {
    return({
      openSide: false,
      pages: [
+       {label: "Top", path: "/"},
+       {label: "page1", path: "/page1"}
       ]
    });

簡単なUI追加

さて、UIを追加していこう。作業内容はOnsenUI のパーツを足していくだけだ。 まずは、アクションボタンを追加し、それっぽい画面を作成してみよう。

ここからは部品ごとに解説を加えていくので、 完成形のソースコード を参照しながら見ていただきたい。

Page1.vueのtemplateセクションに以下を追加する。

<v-ons-fab
  position="bottom right"
  @click="show = true">
  <v-ons-icon icon="md-plus"></v-ons-icon>
</v-ons-fab>

これでUIを追加できる。 またクリック時の挙動として、 showtrue にし、 のちほど用いるモーダルの表示を制御している。 show をscriptセクションの中で宣言する。

data () {
  return {
    show: false
  }
},

こうすることで、template内で用いる変数を javasciptから制御することができるのだ。 これを双方向バインディングというらしい。

次にポップアップモーダルを追加しよう。 templateには以下を追加する。

<v-ons-alert-dialog modifier="rowfooter"
                    :visible.sync="show">
  <span slot="title">入力画面</span>
  <div class="center">
    <v-ons-input placeholder="ひとこと" float
                 v-model="message" />
  </div>
  <template slot="footer">
    <v-ons-alert-dialog-button @click="show = false">
      キャンセル
    </v-ons-alert-dialog-button>
    <v-ons-alert-dialog-button @click="handleOk">
      追加
    </v-ons-alert-dialog-button>
  </template>
</v-ons-alert-dialog>

まず、 =:visible.sync= に show を設定することで、 全体の表示・非表示を設定することができる。

また、 v-ons-input がテキスト入力のフォームであるが、 ここに v-model という属性に message が設定されている。 これも双方向バインディングである。 テキストの入力結果がここへリアルタイムに格納される。 v-ons-alert-dialog-button には @clickhandleOk が設定されているが、これはトリガーだ。 ここで設定されるメソッドが発火される。

では、scriptセクションを見てみよう。

data () {
  return {
    messages: [],
    message: "",
    show: false
  }
},
methods: {
  handleOk () {
    this.messages.push(this.message);
    this.message = "";
    this.show = false;
  }
}

show の他に messagesmassage が宣言されている。 messages は後ほど用いるが、 message は先ほどtemplate 内で宣言された変数だ。ここにフォームの入力内容が入る。 handleOk でフォームの内容を messages につっこんでいるということだ。

最後に入力内容をリストに表示してみよう。 templateセクションに以下を追加する。

<v-ons-list modifier="longdivider">
  <v-ons-list-item v-for="mes in messages">
    {{mes}}
  </v-ons-list-item>
</v-ons-list>

v-ons-list-item がアイテムを格納する構造であるが、 これを v-for で宣言される繰り返し文で動的に追加される。

完成形のデモを見てみよう。

data/vue/app-mov.gif

デプロイ

できたアプリをデプロイするには以下のコマンドを使用する。

PS C:\Users\me\vue-project> npm run build

これで dist 以下にファイルが展開される。 このファイルをサーバにアップロードすることで使用可能となる。

アップロードした後、みんなにURLを教えてデモを配布しよう!

総括

モバイルアプリをWebでプロトタイピングするというのは 学習コストを含む導入部分が面倒かもしれない。 しかし、冒頭に挙げたメリットは非常に大きく、 また、PWA が策定されるなど、ネイティブアプリとWebアプリの 垣根はなくなってきているといえ、 モバイル開発者にもWebアプリケーション開発の技術は 必須となってきている。

これをきっかけに勉強を進めるのもいいかもしれないですよ!

参考