Router 使うと簡単に画面遷移するアプリ作れるってことで試してみましょう。 まぁ、Vue.js は公式ドキュメントがとてもよくて、さらには日本語もあって読みやすい!!なので、公式みたらわかるようになってるのですが、とりあえず試してみたログということで。
ちなみに Vue Router のドキュメントはこちら
Router のないプロジェクトを Vue CLI で作ろう
とりあえずサクッと作ります。Router のない TypeScript のプロジェクトを作りました。とりあえず現状の最新の Vue CLI 3.9.2 でやってます。
$ vue create router-sample
オプションはこんな感じで
Use class-style component syntax? は、No で行きます。それ以外は Enter 連打で。
Router を導入
ではプロジェクトが出来たので Router を導入していきます。プロジェクトのフォルダーで以下のコマンドでインストールします。
npm i vue-router
そして src/main.ts
で Router を使うようにします。とりあえずの動作確認なので各種ページは適当に
import Vue from 'vue'; import Router, { RouteConfig } from 'vue-router'; import App from './App.vue'; Vue.use(Router); // これで vue-router を使うということを明示 Vue.config.productionTip = false; // とりあえずのコンポーネント const Home = Vue.extend({ render() { return this.$createElement('h2', 'Home'); }, }); const About = Vue.extend({ render() { return this.$createElement('h2', 'About'); }, }); // ルートを定義 const routes: RouteConfig[] = [ { path: '/', component: Home }, { path: '/about', component: About}, ]; const router = new Router({ routes, }); new Vue({ router, render: (h) => h(App), }).$mount('#app');
そして、src/App.vue
で Router 系のタグを入れます。
<template> <div id="app"> <h1>Router sample</h1> <div> <router-link to='/'>Home</router-link> <router-link to='/about'>About</router-link> </div> <router-view></router-view> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ name: 'app', }); </script>
ちゃんとしたコンポーネントにする
src/components
フォルダーに Home.vue
と About.vue
を追加して以下のようにします。
<template> <div> <h2>Home</h2> <router-link to="/about">About ページへ</router-link> </div> </template> <script lang="ts"> import Vue from 'vue' export default Vue.extend({ }); </script>
<template> <div> <h2>About</h2> <button @click="goBack">Go back</button> </div> </template> <script lang="ts"> import Vue from 'vue' export default Vue.extend({ methods: { goBack(e: MouseEvent) { this.$router.back(); }, }, }); </script>
実行すると初期状態で Home
が表示されて…
そして src/main.ts
でこのコンポーネントを使うように変更します。
import Vue from 'vue'; import Router, { RouteConfig } from 'vue-router'; import App from './App.vue'; import Home from './components/Home.vue'; import About from './components/About.vue'; Vue.use(Router); Vue.config.productionTip = false; const routes: RouteConfig[] = [ { path: '/', component: Home }, { path: '/about', component: About}, ]; const router = new Router({ routes, }); new Vue({ router, render: (h) => h(App), }).$mount('#app');
これでとりあえず動くはずです。
ページ内のリンクやボタンも動きます。コード補完も効いて書き味もばっちりです!!
ハッシュじゃない感じで
URL をみてみると http://localhost:8080/#/about
みたいにハッシュが使われてます。これでもいいのですが HTML5 histry モードというのもあって URL が綺麗になります。適用の仕方は簡単で src/main.ts
の Router をインスタンス化しているところで mode を history に設定するだけです。
以下のような感じで。
import Vue from 'vue';
import Router, { RouteConfig } from 'vue-router';
import App from './App.vue';
import Home from './components/Home.vue';
import About from './components/About.vue';
Vue.use(Router);
Vue.config.productionTip = false;
const routes: RouteConfig[] = [
{ path: '/', component: Home },
{ path: '/about', component: About},
];
const router = new Router({
mode: 'history', // これを追加
routes,
});
new Vue({
router,
render: (h) => h(App),
}).$mount('#app');
動かしてみると URL が綺麗になった!
こうした時の注意点はデプロイ先で失敗したリクエストも全て index.html に回すするようにする必要があります。
URL にパラメーターも渡したい
http://localhost:8080/users/1
みたいにして 1 を渡したいってことがよくありますね。これもしてみましょう。
src/main.ts
のルートの定義をちょっと追加します。
import Vue from 'vue'; import Router, { RouteConfig } from 'vue-router'; import App from './App.vue'; import Home from './components/Home.vue'; import About from './components/About.vue'; import Users from './components/Users.vue'; Vue.use(Router); Vue.config.productionTip = false; const routes: RouteConfig[] = [ { path: '/', component: Home }, { name: 'users', path: '/users/:id', component: Users}, { path: '/about', component: About}, ]; const router = new Router({ mode: 'history', // これを追加 routes, }); new Vue({ router, render: (h) => h(App), }).$mount('#app');
/users/:id
とパスを定義してるの点と、あとでプログラムからパラメーターを渡して画面遷移するために name
を設定している点がポイントです。
src/components/Users.vue
は以下のような感じです。
<template> <div> <h2>User</h2> <div v-if="loading">loading...</div> <div v-if="!loading">{{ id }}</div> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ data() { return { loading: false, id: 0, }; }, async created() { // 初回 await this.fetchData(); }, async beforeRouteUpdate(to, from, next) { // users/1 から users/2 みたいにコンポーネントが再利用される時用 await this.fetchData(); next(); }, methods: { async fetchData() { this.loading = true; await new Promise((r) => { setInterval(r, 1000); }); this.loading = false; this.id = parseInt(this.$route.params.id, 10); }, }, }); </script>
これで http://localhost:8080/users/22
みたいな URL にアクセスすると以下のような表示になります。スクショにはとってませんが、ちゃんと 1 秒くらい loading... と表示されます。
src/components/About.vue
をちょっと変更してテキストに文字入れて users に画面遷移するようにしてみました。
<template> <div> <h2>About</h2> <button @click="goBack">Go back</button> <form @submit.prevent="submit"> <input type="text" v-model="id" /> <input type="submit" value="画面遷移" /> </form> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ data() { return { id: '', }; }, methods: { goBack(e: MouseEvent) { this.$router.back(); }, submit() { this.$router.push({ name: 'users', params: { id: this.id }}); }, }, }); </script>
this.$router.push
で簡単にいい感じに画面遷移出来ますね。素敵。
About を開いて適当に数字を入れて…
画面遷移ボタンを押すか、Enter キーで users/:id
で定義した Users コンポーネントに遷移します。
おまけ
画面遷移と関係ないですが Home コンポーネントに入力した文字をそのまま大文字にして出すような処理も追加してみました。ルーター使っても普通に動いていいね。
<template> <div> <h2>Home</h2> <router-link to="/about">About ページへ</router-link> <div> <input type="text" v-model="input" /> </div> <div> <span>{{ output }}</span> </div> </div> </template> <script lang="ts"> import Vue from 'vue'; export default Vue.extend({ data() { return { input: '', output: '', }; }, watch: { input(val: string) { this.output = val.toUpperCase(); }, }, }); </script>
まとめ
簡単でいい感じですね!!
試した時のソースコードなので整理されてないですが GitHub に上げてます。