ここ数年、ネイティブアプリを脅かすと噂されるくらい話題になってきているSPAですが、私は比較的最近になってSPAという技術を触り始めました。SPAといってもWebブラウザ上で動くものもあればスマホアプリ上(WebViewやUIWebviewなど)で動くものもあります。私が最初に触れたのはスマホアプリ上で動くものでした。
しかし、実際にスマホで操作してみると、タップの反応速度がやたらと遅い...。というわけで考えられる原因とその対応策をまとめてみました。
##前提条件
###そもそもSPAとは
この記事をご覧になっている方なら既にご存知だとは思いますが、SPAは「Single Page Application」の略であり、「同一のページ内で処理されるコンテンツ切り替えの手法」です。つまり言語やプラットフォームに依存することはなく、そのような動きが実現できればSPAと言えます。メリットとしてはページ遷移が発生しない分、コンテンツの切り替え速度が向上しUXの改善になることが一番大きいと思います。
その利点からSPAを使ってスマホアプリを作るという考え方もあります。スマホアプリ内のブラウザ機能を使ってSPAで作られたページにアクセスすることによって、ネイティブアプリと遜色ない操作感を実現してしまおうというのです。今回はまさにこの考え方に基づいたスマホアプリ上で動くSPAに触れたお話です。
###開発環境
開発環境としては以下の通りです。
-Mac(High Sierra)
-PHP 7.1
-Laravel 5.4
-Vue.js 2.5.7
##スマホ実機のタップの反応速度がやたら遅い
開発そのものはリファレンスや技術系の記事を拝見させていただきながら比較的順調に進んでいました。しかし、ある程度開発を進め、スマホ実機でテストをしてみたところ、思わぬ課題が見つかりました。冒頭に既に書いてありますが、タップの反応速度が異常に遅かったのです。MacのChromeではあんなにサクサク動いていたのに。
##原因はスマホのクリック判定か
いろいろ調べてみたところ、原因はスマホのクリック判定にある可能性が高いと判断しました。スマホは「touchstart」を経て「touchend」の処理を終えることで「click」の判定がされます。そしてどうやらスマホのアプリ内ブラウザではクリック判定がどうも遅いようなのです。
##fastClick.jsによる対応
検索していたら下記の記事を見つけました。
スマホのclickを100倍高速化する「fastClick.js」
http://milkyshade.com/html-css/501/
これはタップした瞬間に即座に位置を把握し、同一場所でのクリック動作を命令するJSライブラリのようです。通常のスマホサイトとかであればこれを使えば十分そうです。しかし、今回はVue.jsを使ったSPAなので予期せぬ動きをするかもしれない。実際入れてみたら、タップしていない箇所のイベントが発火したり、あまりうまくいきませんでした。
##vue2-touch-eventsによる対応
さらに調べてみると、なんとVue.jsには「vue2-touch-events」というものが存在していました。
https://www.npmjs.com/package/vue2-touch-events
以下のコマンドですぐにインストールできます。
npm i -S vue2-touch-events
import Vue from 'vue'
import Vue2TouchEvents from 'vue2-touch-events'
Vue.use(Vue2TouchEvents)
そして発火したい箇所にv-touch属性を加えればひとまず動作します
<template>
<div class="menu-btn" v-touch="toggleMenu">
<span>menu</span>
</div>
</template>
<script>
export default {
methods: {
toggleMenu(){
alert('発火!');
}
}
}
</script>
しかし、vue-routerなどでルーティングをしている場合、router-linkなどのタグが使えなくなってしまう問題が出てきます。
###v-touchはv-onのようには使えない
router-linkの代替手段としては、呼び出したメソッド内でrouterオブジェクトを利用してpushするのがよさそうです。
※routerオブジェクトについてはこちら
https://router.vuejs.org/ja/api/route-object.html
なので、こちらのコードを書いてみました。
<template>
<p v-touch="pageLink('/testpage')">リンクテキスト<p>
</template>
<script>
export default {
methods: {
pageLink(link){
this.$router.push( link );
}
}
}
</script>
ところが、これで試したところ全く反応がなく、エラーすら吐き出されない状態...。もう一度vue2-touch-eventsのリファレンスを確認してみると、
if you want to add extra parameters for v-touch, you can't do that like v-on.
(https://www.npmjs.com/package/vue2-touch-events#others)
と書かれていました。なるほど。つまりv-onのようにはいかないと。
引数を利用して動作させたい場合は、return functionを利用する必要があるようです。というわけで下記の内容に修正しました。
<template>
<p v-touch="pageLink('/testpage')">リンクテキスト<p>
</template>
<script>
export default {
methods: {
pageLink(link, obj = this){
return function() {
obj.$router.push( link );
}
}
}
}
</script>
これで正常に動作しました。実機で確認してもスマホアプリをそう変わらない速度感での操作が可能となりました。あとはこれをmixinするなどして共通化すれば汎用的に利用できますね。
※mixinについてはこちら
https://jp.vuejs.org/v2/guide/mixins.html
もし同じようにスマホアプリ上でのタップの反応速度がよくない場合は、ぜひお試しください。