はじめに Vue.js を使ってネイテイブアプリの開発が可能な NativeScript-Vue について整理した。
セットアップから、Vue Router を使ったページ遷移、モーダルの表示などについて。
目次 はじめに 環境・条件 詳細 NativeScript-Vueをうまく使うためには 事前準備 NatvieScript のセットアップ クイックスタート アプリの実行(tns run) iOS Android ルーティング モーダル Android と iOS の差分 スワイプバック 画面遷移/モーダル表示 時の挙動 アプリのプレビュー(tns preview) その他のコマンド tns device: デバイス一覧 その他・メモ 他のフレームワーク 所感 参考文献
環境・条件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 $ sw_vers ProductName: Mac OS X ProductVersion: 10.15.3 BuildVersion: 19D76 $ node -v v12.7.0 $ npm -v 6.10.3 $ npm ls -g @vue/cli @vue/cli-init /Users/foo/.nodenv/versions/12.7.0/lib ├── @vue/cli@4.2.2 └── @vue/cli-init@4.2.2 $ tns --version 6.4.0 $ grep -C1 version package.json "tns-ios" : { "version" : "6.0.1" }, "tns-android" : { "version" : "6.0.0" } $ sudo gem list --local | grep -e xcode -e cocoa cocoapods (1.8.4) cocoapods-core (1.8.4) cocoapods-deintegrate (1.0.4) cocoapods-downloader (1.3.0) cocoapods-plugins (1.0.0) cocoapods-search (1.0.0) cocoapods-stats (1.1.0) cocoapods-trunk (1.4.1) cocoapods-try (1.1.0) xcodeproj (1.14.0) $ pip -V pip 19.1.1 from /.../.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages/pip (python 3.6) $ pip show six Name: six Version: 1.10.0
iPhone 11 Pro: iOS 13.3
Android HUAWEI nova lite 2: Android 9 (ビルド 9.1.0.160)
詳細 リポジトリ: 17number/nativescript-vue-tutorial
NativeScript-Vueをうまく使うためには はじめに に以下の記載
NativeScript CLI を使うこと
NativeScript UI コンポーネントを知ること
事前準備 NatvieScript のセットアップ 以下を参考に各種インストールなどを行う。 ※本記事では省略している箇所もあるので、できるだけ公式ドキュメントを見ることを推奨。(省略箇所は「省略」と記載)
Node.js インストール(省略)。 ※v10.x 以降でないとダメかも 参考: Step 1: Install Node.js
nativescript-cli をインストール。(tns
コマンドが実行できない場合は未インストール) 参考: Step 2: Install the NativeScript CLI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ tns -bash: tns: command not found $ npm i -g nativescript ... Do you want to help us improve NativeScript by automatically sending anonymous usage statistics? We will not use this information to identify or contact you. You can read our official Privacy Policy at ? http://www.telerik.com/company/privacy-policy Yes If you are using bash or zsh, you can enable command -line completion. ? Do you want to enable it now? Yes Restart your shell to enable command auto-completion. I agree to receive email communications from Progress Software in the form of the NativeScript Newsletter. Consent may be withdrawn at any time. ? Input your e-mail address to agree or leave empty to decline: ... + nativescript@6.4.0 $ tns --version 6.4.0
macOS 向け 以降は macOS 向けの設定手順。インストールされていないものがあればインストールする。 参考: NativeScript Advanced Setup — macOS - NativeScript Docs
Homebrew のインストール 参考: Advanced Setup Steps
1 $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install) "
iOS 開発用 iOS 開発に必要な gem などのインストール
1 2 3 4 5 6 7 8 9 10 11 $ sudo gem list --local | grep -e xcode -e cocoa cocoapods (1.8.4) cocoapods-core (1.8.4) ... xcodeproj (1.14.0) $ sudo gem install xcodeproj $ sudo gem install cocoapods $ pod setup
インストールが漏れていると、ビルド時に Command failed: ruby -e "require 'xcodeproj';
のエラーが出る。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ pip -V pip 19.1.1 from /.../.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages/pip (python 3.6) $ sudo eacy_install pip $ pip show six Name: six Version: 1.10.0 Location: /.../.pyenv/versions/anaconda3-5.0.0/lib/python3.6/site-packages $ pip install six
Android 開発用 Android 開発に必要な JDK などのインストール(省略)
クイックスタート クイックスタート - NativeScript-Vue
vue-cli
, vue-cli-init
はインストール済だったが、古かったので念のためアップデート。
1 2 3 4 5 6 7 8 9 10 11 $ npm ls -g @vue/cli @vue/cli-init /Users/foo/.nodenv/versions/12.7.0/lib ├── @vue/cli@3.10.0 └── @vue/cli-init@4.0.4 $ npm i -g @vue/cli @vue/cli-init $ npm ls -g @vue/cli @vue/cli-init /Users/foo/.nodenv/versions/12.7.0/lib ├── @vue/cli@4.2.2 └── @vue/cli-init@4.2.2
vue init nativescript-vue/vue-cli-template <app name>
でプロジェクト生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ vue init nativescript-vue/vue-cli-template nativescript-vue-tutorial ? Project name nativescript-vue-tutorial ? Project description A native application built with NativeScript-Vue ? Application name NativeScript-Vue Application ? Unique application identifier com.example.nativescript.application ? Project version 1.0.0 ? Author foo <hoge@fuga.com> ? License MIT ? Select the programming language javascript ? Select a preset (more coming soon) Simple ? Install vuex? (state management) No ? Install vue-devtools? Yes ? Color scheme default vue-cli · Generated "nativescript-vue-tutorial" . vue-cli · cd nativescript-vue-tutorial vue-cli · npm install vue-cli · tns preview vue-cli · vue-cli · tns run
内容を決定したら、最後に表示されるガイドに従ってコマンド実行。
1 2 $ cd nativescript-vue-tutorial $ npm install
ビルドや実行時の主なコマンドは下記。
1 2 3 4 5 6 7 8 9 10 11 tns preview tns run tns debug <platform> tns build <platform> --env.production
各コマンドの細かい違いは公式ドキュメントを参照。(オプション設定次第で意味が変わってくるので。。。)
アプリの実行(tns run) アプリを実際に実行してみる。
iOS iOS デバイスを接続して tns run ios --bundle
1 2 3 4 5 6 $ tns run ios --bundle ... ? Found multiple development teams, select one: Hoge.Company (ABCDE01234) ? Do you want to make teamId: ABCDE01234 a persistent choice for your app? Yes, set the DEVELOPMENT_TEAM setting in build.xcconfig file ...
成功するとアプリがインストールされ、起動する。以下のような画面が表示されれば OK。
なお、画像中に書いてる通りダークモードだと「何も表示されていないように見える」。文字色などを変更する場合は app/components/App.vue
を修正すると良い。
1 2 3 4 5 6 7 8 9 <style scoped> ... .message { ... - color: #333333; + color: #fff; # 白文字にすると Android で白背景の場合に見えなくなるので、別の色にするのが良いかも。 } </style>
Android iOS と同様にデバイスを接続後 tns run android --bundle
を実行。
1 2 $ tns run android --bundle ...
ルーティング 2020/02/21 現在、Vue Router は NativeScript-Vue では未対応なので、自力で組み込む($navigateTo
, $navigateBack
を使う)必要がある。
Currently, integration with Vue Router is unsupported. Until the team resolves the issue, please use manual routing .Vue Router (Unsupported) より
$navigateTo
, $navigateBack
の使い方は Manual Routing に書かれている。
ルーティング試行時のコミット:
app/components/App.vue
を変更、ボタンタップで HelloWorld.vue
に遷移させる。遷移には $navigateTo
を利用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <template > <Page > <ActionBar title ="Home" /> <StackLayout > <Button text ="Go to HelloWorld" @tap ="goToHelloWorld" /> </StackLayout > </Page > </template > <script > import HelloWorld from '@/pages/HelloWorld' export default { name: 'home' , methods: { goToHelloWorld() { console .log('tapped button "go to helloworld"' ); this .$navigateTo(HelloWorld); }, }, } </script >
app/components/App.vue
を複製して app/pages/HelloWorld.vue
を作成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <template > <Page > <ActionBar title ="Hello world!" /> <StackLayout > <Button text ="Go to home" @tap ="goToHome" /> </StackLayout > </Page > </template > <script > import home from '@/components/App' export default { name: 'HelloWolrd' , methods: { goToHome() { console .log('tapped button "go to home"' ); this .$navigateTo(home); }, }, } </script > <style scoped > ActionBar { background-color : #53ba82 ; color : #ffffff ; } Button { color : #53ba82 ; } </style >
モーダル モーダルの表示は Modal View Navigation に書かれている通り、$showModal
を使う。
モーダル表示 試行時のコミット: 1d4a150705d79f9a189840e24d0854933d4ced08
app/pages/Modal.vue
を作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <template > <Page > <ActionBar :title ="title" /> <StackLayout > <Button :text ="text" @tap ="closeModal" /> </StackLayout > </Page > </template > <script > export default { name: 'Modal' , props: { from : '' , }, data() { return { title: 'This is modal' , text: 'Close modal' , } }, methods: { closeModal() { console .log('tapped button "close modal"' ); this .$modal.close(); }, }, mounted() { if (this .from !== '' ) { this .title += ` from ${this .from } ` this .text += ` from ${this .from } ` } }, } </script > <style scoped > ActionBar { background-color : #53ba82 ; color : #ffffff ; } .message { text-align: center; color : #53ba82 ; } </style >
app/pages/HelloWorld.vue
を編集、モーダル表示用のボタンを追加。
プロパティ(props
)を渡すときは、 { props: { id: 1 } }
のように指定。また、フルスクリーンにしたい場合は { fullscreen: true }
で指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <template > <Page > <ActionBar title ="Hello world!" /> <StackLayout > <Button text ="Go to home" @tap ="goToHome" /> <Button text ="Show modal" @tap ="showModal" /> <Button text ="Show modal(Full Screen)" @tap ="showModalFullScreen" /> </StackLayout > </Page > </template > <script > import home from '@/components/App' import Modal from '@/pages/Modal' export default { name: 'HelloWolrd' , methods: { goToHome() { console .log('tapped button "go to home"' ); this .$navigateTo(home); }, showModal() { console .log('tapped button "show modal"' ); this .$showModal(Modal, { props : { from : 'HelloWorld' }, }); }, showModalFullScreen() { console .log('tapped button "show modal"' ); this .$showModal(Modal, { fullscreen : true , props : { from : 'HelloWorld' }, }); }, }, } </script >
Android と iOS の差分 ここまでのコードで Android で動作確認したところ下記のような挙動になる。
iOS との差分としては下記
スワイプバックができない
画面遷移時の挙動
モーダル表示時の挙動
スワイプバック Props - Page を見ると、下記の通り iOS だけ対応している模様。
enableSwipeBackNavigation (iOS-only) Gets or sets whether the page can be swiped back on iOS.
Android で同様の挙動を実現するには Swipe - Gestures を参考に実装する必要があると思われる。
ページ全体に swipe
イベントのハンドラを設定するには以下のようにすれば良い。 コミット: 3001b85c97bfe60f29ce3b9b734f40035408a284
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <template > <Page @swipe ="onSwipe" > ... </Page > </template > <script > ... export default { ... methods: { ... onSwipe(args) { let direction; switch (args.direction) { case 1 : direction = 'left to right' ; break ; case 2 : direction = 'right to left' ; break ; case 4 : direction = 'down to up' ; break ; case 8 : direction = 'up to down' ; break ; } console .log(`onSwipe, direction: ${direction} (${args.direction} )` ); }, }, } </script >
スワイプ方向は args.direction
で取得可能で、値と方向の対応関係は下記の通り。
1
: 左から右
2
: 右から左
4
: 下から上
8
: 上から下
画面遷移/モーダル表示 時の挙動 Passing props to the target component に記載のある通り、画面遷移はトランジションが設定可能。
1 2 3 4 5 this .$navigateTo(<component name > , { transition: {}, transitioniOS: {}, transitionAndroid: {}, });
NavigationEntry , NavigationTransition , NavigationTransition の実装 を見ると、デフォルトのトランジションで指定可能なのは下記。
curl
(same as curlUp) (iOS only)
curlUp
(iOS only)
curlDown
(iOS only)
explode
(Android Lollipop(21) and up only)
fade
flip
(same as flipRight)
flipRight
flipLeft
slide
(same as slideLeft)
slideLeft
slideRight
slideTop
slideBottom
iOS と同じようなトランジションにしたい場合は、 transitionAndroid: { name: 'slide' }
を指定すると良い。
1 2 3 this .$navigateTo(HelloWorld, { transitionAndroid: { name : 'slide' }, });
また、モーダルについては以下を参考に animated: true, streched: true
を指定することで、iOS と似たような感じにはできた。
1 2 3 4 5 this .$showModal(Modal, { animated: true , stretched: true , props: { from : 'HelloWorld' }, });
変更後の挙動は以下の通り。
Android のトランジション対応 コミット: e282057b239ddcbe42e702a1d597c90f044f6d50
アプリのプレビュー(tns preview) tns preview
を実行すると QR コードが表示される。
デバイスで実行するために NativeScript Playground app と、NativeScript Preview app が必要。
NativeScript Playground
NativeScript Preview
tns preview
で表示された QR コードを、NativeScript Playground で読み込むと、ビルドとデプロイが行われる。
Install vue-devtools? Yes
で tns preview
を実行すると、TypeError: undefined is not an object (evaluating 'socket.on')
のエラーになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ... 2020-02-12 11:50:45.872 nsplaydev[90588:6859089] JavaScript error: file:///app/vendor.js:3540:2296: JS ERROR TypeError: undefined is not an object (evaluating 'socket.on' ) 2020-02-12 11:50:45.873 nsplaydev[90588:6859089] PlayLiveSync: Uncaught Exception 2020-02-12 11:50:45.873 nsplaydev[90588:6859089] *** JavaScript call stack: ( 0 UIApplicationMain@[native code] 1 run@file:///app/tns_modules/@nativescript/core/application/application.js:312:26 2 @file:///app/tns_modules/nativescript-vue/dist/index.js:14050:18 3 @file:///app/vendor.js:5669:25 4 @file:///app/bundle.js:317:10 5 ./main.js@file:///app/bundle.js:321:34 6 __webpack_require__@file:///app/runtime.js:751:34 7 checkDeferredModules@file:///app/runtime.js:44:42 8 webpackJsonpCallback@file:///app/runtime.js:31:39 9 anonymous@file:///app/bundle.js:2:61 10 evaluate@[native code] 11 moduleEvaluation@:1:11 12 @:2:1 13 asyncFunctionResume@:1:11 14 @:24:9 15 promiseReactionJob@:1:11 ) ...
app/main.js
の VueDevtools
に関連するコードを削除やコメントアウトするとエラーが解消する。 参考: undefined is not an object (evaluating ‘socket.on’) · Issue #135 · nativescript-vue/vue-cli-template
1 2 3 4 5 6 7 -import VueDevtools from 'nativescript-vue-devtools' +// import VueDevtools from 'nativescript-vue-devtools' if(TNS_ENV !== 'production') { - Vue.use(VueDevtools) + // Vue.use(VueDevtools) }
その他のコマンド tns device: デバイス一覧 1 2 3 4 5 6 7 8 $ tns device Connected devices & emulators Searching for devices... ┌───┬─────────────┬──────────┬───────────────────────────┬────────┬───────────┬─────────────────┐ │ │ 1 │ foo iPhone │ iOS │ 01234567-0123456789ABCDEF │ Device │ Connected │ USB │ └───┴─────────────┴──────────┴───────────────────────────┴────────┴───────────┴─────────────────┘
その他・メモ 他のフレームワーク Vue.js でネイテイブアプリを作るためのフレームワークは、他にも以下のものがある。Vue Native は開発が停止しているかも。
Vue Native の方は「Vue Native → React Native → Native App」という流れでアプリを生成しているっぽい。 参考: Vue Native: Vue.js を React Native に変換してネイティブアプリを作れるように
所感 Vue.js を利用できるが、HTML タグを NativeScript の独自タグに置き換えたり、レイアウトについて知る必要がありそうだったりで、既存の Vue.js + Cordova などのハイブリッドアプリからの移行はそれなりに学習が必要そう。
参考文献
関連記事