BETA

beforeRouteEnterのnextに渡したコールバックはmountedよりも後に呼ばれる

投稿日:2019-05-06
最終更新:2019-05-06

ルーターフックとも呼ばれているVue Routerのナビゲーションガードですが、
そのうちのよく使うbeforeRouteEnternextコールバックの実行タイミングが、自分が思っていたのと違っていました。

検証

// Foo.vue  
beforeRouteEnter(to, from, next) {  
  console.log("beforeRouteEnter");  

  next(component => {  
    console.log("beforeRouteEnter:next"); // nextコールバックの実行タイミングを調べる  
  });  
},  

created() {  
  console.log("created");  
},  

mounted() {  
  console.log("mounted");  
},  


上の結果から、nextに渡したコールバック関数はVueライフサイクルフックのmountedよりも後に実行されているのが分かります。

beforeRouteEnterでデータをfetchする場合は気をつけよう

beforeRouteEnterはルートが確立する前、コンポーネントが描画される前にフックされます。
コンポーネントが描画される前にデータを取得したい場合に、よくbeforeRouteEnterは使われるようです。

beforeRouteEnterはコンポーネントのthisにアクセスできませんが、nextコールバックの引数からコンポーネントインスタンスにアクセスできます。

この beforeRouteEnter ガードは this へのアクセスはできないです。なぜならば、ナビゲーションが確立する前にガードが呼び出されるからです。したがって、新しく入ってくるコンポーネントはまだ作られていないです。

しかしながら、 next にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。
引用: コンポーネント内ガード | Vue Router

beforeRouteEnter (to, from, next) {  

  next(vm => {  
    // vm を通じてコンポーネントインスタンスにアクセス  
  })  
}  

これをふまえて、beforeRouteEnterで取得したデータをnextコールバック内でコンポーネントのdataオブジェクトのプロパティに代入し、createdmountedなどのライフサイクルフックでアクセスしたいと思うかもしれません。

export default {  
  data: {  
    obj: null  
  },  

  async beforeRouteEnter(to, from, next) {  
    const objectData = await fetch();  

    next(component => {  
      component.obj = objectData; // 取得したデータを obj に代入する  
    });  
  },  

  created() {  
    console.log(this.obj); // objにアクセスする  
  },  

  mounted() {  
    console.log(this.obj); // objにアクセスする  
  }  
};  

しかし、冒頭の検証でnextコールバックはmountedの後に実行されているのが分かっています。
つまり、objに代入する処理はcreatedmountedの時点で実行されていません。
createdmountedobjにアクセスしても空の値にアクセスしていることになります。

export default {  
  data: {  
    obj: null  
  },  

  async beforeRouteEnter(to, from, next) {  
    const objectData = await fetch();  

    next(component => {  
      component.obj = objectData; // mountedの後に実行される  
    });  
  },  

  created() {  
    console.log(this.obj); // null  
  },  

  mounted() {  
    console.log(this.obj); // null  
  }  
};  

Vuexを使えば解決できる

これはVuexで管理するなら解決できます。

beforeRouteEnter内で、取得したデータをVuexのステートに入れます。
createdmountedからVuexのステートにアクセス。

こうすれば、createdmountedよりも早くフックされるbeforeRouteEnterで、Vuexのステートに取得したデータを入れて、その後にライフサイクルフックでVuexのステートからデータを取ってくれば良いです。

最後に

beforeRouteEnterで取得したデータをmountedでゴニョっとしたい!と思っていたのですが、少しつまずいてしまいました。

幸いにも、取得するデータはいくつものコンポーネントで使うので、Vuexでデータにアクセスできるようにしていて、Vuex経由でmountedでデータをゴニョる事ができました。

今後、beforeRouteEnterでデータを取ってnextコールバックを使う場合は気をつけていきたいです。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

プログラミング初心者です。作ったものとかメモ代わりのTipsとか投下していきます~。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!