しおしお

IntelliJ IDEAのことなんかを書いてます

Vue.jsのrouteパラメータを$router.paramsではなくpropsで受け取る

やりたいこと

routeパラメータを、コンポーネント側で$router.paramsを使って取り出すのではなく、propsに代入してもらいコンポーネントvue-routerに依存しないようにする。

お試しコード

ルート定義

ルート定義する際に、 props: true を追加してあげます。 これをすることで、コンポーネントのプロパティでrouteパラメータを受け取れるようになります。

const routes = [
  { path: '/hello/:message', name: 'hello', component: HelloWorld, props: true }
]

コンポーネント側のコード

routeパラメータを受け取るプロパティを定義してあげるだけですね。 検証用に、createdでプロパティの内容を出力してみます。

import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private message!: string;

  created() {
    console.log('message', this.message)
  }
}

動かしてみた結果

hello/hogeにアクセスすると、コンソールにhogeが出力されているので、プロパティでrouteパラメータが受け取れていることが確認できます。 f:id:sioiri:20200209070057p:plain

お試しコード(任意のオブジェクトで受け取る)

propsでrouteパラメータの値をオブジェクトで受け取ってみたいと思います。

コンポーネント側のコード

プロパティの型を、stringからMessage型に変更してみます。 Message型には、idとmessageのプロパティを定義してみます。

import { Component, Prop, Vue } from 'vue-property-decorator';

interface Message {
  id: number
  message: string
}

@Component
export default class HelloWorld extends Vue {
  @Prop() private message!: Message

  created() {
    console.log('message', this.message)
  }
}

ナビゲーションのコード

ナビゲーション時に、paramsにコンポーネントが要求するオブジェクトを設定してあげます

<router-link :to="{name: 'hello', params:{message: {id: 999, message: 'メッセージ'}}}">Hello!!</router-link>

動かしてみた結果

コンソールにrouteパラメータが出力出来ているので、オブジェクトでの受け渡しが出来ていることが確認できますね。 f:id:sioiri:20200209071703p:plain

Vue-Routerへの依存をなくした場合のメリット

テストを考えた場合、コンポーネントがVue-Routerに依存している場合($router.paramsへ依存している場合)、モックを定義してあげないとテストができなくなってしまいます。 $router.paramsを使わずにプロパティー経由で受け取った場合、下のテストコードのように、propsDataをセットアップするだけでテストが実行可能となります。

describe('HelloWorld', () => {
  it('test', () => {
    const wrapper = shallowMount(HelloWorld, {
      propsData: {
        message: {
          id: 1, message: 'メッセージ'
        }
      }
    })

    expect(wrapper.text()).toContain('メッセージ')
  })
})

$route.paramsに依存している場合は、下のコードのようにmocksでデータをセットアップする必要があります。 ちょっとめんどくさいのと、コンポーネント単体だけじゃなくルート定義などを意識したテストコードになるのが嫌ですね。

describe('HelloWorld', () => {
  it('test', () => {
    const wrapper = shallowMount(HelloWorld, {
      mocks: {
        $route: {
          params: {
            message: 'メッセージ'
          }
        }
      }
    })

    expect(wrapper.text()).toContain('メッセージ')
  })
})