LoginSignup
59

More than 5 years have passed since last update.

vue.jsとpage.jsの組み合わせでルーティングをするときのサンプル

Last updated at Posted at 2014-11-20

はじめに

実用的なSPAの開発プロジェクトでvue.jsを採用するならば,何かしらのルーティングライブラリを一緒に使うことになるかと思います。

今回,シンプルなルーティングライブラリであるpage.jsを試したみたところ,page.js側で受け取ったパラメータをコンポーネントに渡す方法に少し悩んだので,こんな書き方はどうでしょう?という意味でサンプルコードを紹介します。

前提コード

homeexampleという2つのコンポーネントがあり,それぞれ/というパスと/example/:qというパスに対応しています。exampleの方はパラメータqを受け取り,パラメータに応じたコンテンツを表示します。

<script src="//cdnjs.cloudflare.com/ajax/libs/vue/0.11.0/vue.min.js"></script>
<script src="//cdn.rawgit.com/visionmedia/page.js/master/page.js"></script>

<div id="container">
  <div v-component="{{main}}">
    <div><a href="/tmp/">home</a></div>
    <div><a href="/tmp/example/hoge">example/hoge</a></div>
    <div><a href="/tmp/example/fuga">example/fuga</a></div>
  </div>
</div>

<script type="text/v-template" id="home">
  <h1>home</h1>
  <content />
</script>
<script type="text/v-template" id="example">
  <h1>example</h1>
  <div>パラメータをここに表示する</div>
  <content />
</script>

<script>
  var app = new Vue({
    el: "#container",
    data: {
      main: undefined
    }
  })
  Vue.component("home", {
    template: "#home"
  })
  Vue.component("example", {
    template: "#example"
  })

  page('/', function(ctx) {
    app.main = "home"
  })
  page('/example/:q', function(ctx) {
    app.main = "example"
    // ctx.paramsをどうやってexampleコンポーネントに渡す?
  })
  page()
</script>

$root経由でパラメータを渡す

まず,ルートViewModelのフィールドとしてparamsを定義します。

  var app = new Vue({
    el: "#container",
    data: {
      main: undefined,
      params: {}
    }
  })

page.jsのコールバックでルートViewModelのparamsに受け取ったパラメータを設定します。

  page('/example/:q', function(ctx) {
    app.params = ctx.params
    app.main = "example"
  })

テンプレート側からは次のようにparamsを参照可能です。

<script type="text/v-template" id="example">
  <h1>example</h1>
  <div>q: {{$root.params.q}}</div>
  <content />
</script>

上記の方法の問題点

$root経由で単純にパラメータを渡す方法だと,受け取ったパラメータを使ってAPIを叩き,返ってきた結果をViewに表示するようなケースでうまく行きません。

具体的には,/example/hogeから/example/fugaに遷移するようなケースで,コンポーネントが持つreadyなどのライフサイクルハンドラが呼ばれないため,APIを叩くコードを処理することができず,困ってしまうことになります。

そこで,この問題を解決する2つの方法を考えました。

vue.jsのイベントシステムを使う

vue.jsにはコンポーネント間でイベントのハンドリングを行うイベントシステムが備わっているので,それを使ってみます。

page.jsのコールバックを次のように書き換えます。

  page('/example/:q', function(ctx) {
    app.main = "example"

    // 初回はまだexampleコンポーネントが生成されていないので
    // 次のタイミングまで待つ
    Vue.nextTick(function() {
      app.$broadcast("example-init", ctx.params)
    })
  })

コンポーネント側では次のようにイベントを受け取ります。

  Vue.component("example", {
    template: "#example",
    data: function() { return {
      params: undefined
    }},
    ready: function() {
      this.$on("example-init", function(params) {
        this.params = params

        // ここでAPIを叩いたり色々やる
      })
    }
  })

テンプレート側からは次のようにparamsを参照可能です。

<script type="text/v-template" id="example">
  <h1>example</h1>
  <div>q: {{params.q}}</div>
  <content />
</script>

直接コンポーネントのメソッドを呼ぶ

もしくは,v-refディレクティブによりコンポーネントに名前を設定し,直接exampleコンポーネントのメソッドを呼ぶ方法もあります。

HTML中のコンポーネントの部分にv-refディレクティブを追加します。

<div id="container">
  <div v-component="{{main}}" v-ref="child">
    <div><a href="/tmp/">home</a></div>
    <div><a href="/tmp/example/hoge">example/hoge</a></div>
    <div><a href="/tmp/example/fuga">example/fuga</a></div>
  </div>
</div>

page.jsのコールバックを次のように書き換えます。

  page('/example/:q', function(ctx) {
    app.main = "example"

    // 初回はまだexampleコンポーネントが生成されていないので
    // 次のタイミングまで待つ
    Vue.nextTick(function() {
      app.$.child.init(ctx.params)
    })
  })

コンポーネント側は次のようにします。

  Vue.component("example", {
    template: "#example",
    data: function() { return {
      params: undefined
    }},
    methods: {
      init: function(params) {
        this.params = params

        // ここでAPIを叩いたり色々やる
      }
    }
  })

おわりに

初心者なりにこんな方法はどうかな?ということで考えてみましたが,もっといいやり方があれば教えていただけると嬉しいです。

ちなみに,記事中には書きませんでしたが,//example/*へのアクセスはサーバー側で適宜リダイレクトしてあげる必要があります。Apacheならば次のような設定を使うことになるでしょう。

<ifModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !index
  RewriteRule (.*) index.html [L]
</ifModule>

関連リンク

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59