はじめに

Vue.js を使ってネイテイブアプリの開発が可能な NativeScript-Vue について整理した。

セットアップから、Vue Router を使ったページ遷移、モーダルの表示などについて。

目次

  1. はじめに
  2. 環境・条件
  3. 詳細
    1. NativeScript-Vueをうまく使うためには
    2. 事前準備
    3. クイックスタート
    4. アプリの実行(tns run)
      1. iOS
      2. Android
    5. ルーティング
    6. モーダル
    7. Android と iOS の差分
      1. スワイプバック
      2. 画面遷移/モーダル表示 時の挙動
    8. アプリのプレビュー(tns preview)
    9. その他のコマンド
      1. tns device: デバイス一覧
  4. その他・メモ
    1. 他のフレームワーク
    2. 所感
  5. 参考文献

環境・条件

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
$ 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"
}
  • 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 コンポーネントを知ること

事前準備

インストール - NativeScript-Vue に書かれている内容をインストールしておく。

nativescript-cli をインストールしていない場合はインストール。(tns コマンドが実行できない場合はインストールされていない)

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

# 匿名での統計情報の送信に同意する場合は Yes
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

# コマンドの自動補完: 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.

# メールで情報を受取る場合は自分のメールアドレスを、不要な場合は空欄のまま Enter
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

# インストール完了後、ターミナル再起動や source コマンドで tns が使えるようになる
$ tns --version
6.4.0

クイックスタート

クイックスタート - 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 # APP ID
? Project version 1.0.0
? Author foo <hoge@fuga.com> # 自分や会社のユーザー情報
? License MIT
? Select the programming language javascript # TypeScript も選択可能
? Select a preset (more coming soon) Simple # 他に TabView, SideDrawer がある
? 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 · # or
vue-cli · tns run

内容を決定したら、最後に表示されるガイドに従ってコマンド実行。

1
2
$ cd nativescript-vue-tutorial
$ npm install

ビルドや実行時の主なコマンドは下記。

1
2
3
4
5
6
7
8
9
10
11
# Preview on device
tns preview

# Build, watch for changes and run the application
tns run

# Build, watch for changes and debug the application
tns debug <platform>

# Build for production
tns build <platform> --env.production

tns runtns debug が開発用、tns preview がプロダクション相当と思われる。

アプリの実行(tns run)

アプリを実際に実行してみる。

iOS

iOS デバイスを接続して tns run ios --bundle

1
2
3
4
5
6
$ tns run ios --bundle
...
# 途中 Xcode の設定について聞かれるので適宜選択
? 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) {
// 1: 左から右, 2: 右から左, 4: 下から上, 8: 上から下
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 コードが表示される。

1
$ tns preview

デバイスで実行するために NativeScript Playground app と、NativeScript Preview app が必要。

tns preview で表示された QR コードを、NativeScript Playground で読み込むと、ビルドとデプロイが行われる。

Install vue-devtools? Yestns 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.jsVueDevtools に関連するコードを削除やコメントアウトするとエラーが解消する。
参考: 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...
┌───┬─────────────┬──────────┬───────────────────────────┬────────┬───────────┬─────────────────┐
# │ Device Name │ Platform │ Device Identifier │ Type │ Status │ Connection Type │
│ 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 などのハイブリッドアプリからの移行はそれなりに学習が必要そう。

参考文献

関連記事