addRoutesによる動的ルーティング


詳細
原文住所:フロントエンド道中、転載は出典を明記してください.
 
以前、Vueベースのバックグラウンドシステム権限制御の実装について述べたが、ルーティング権限の実装構想は、ルーティングジャンプのたびにbeforeフックで判断するのが好きではないため、Vueインスタンスを初期化する前にルーティングをフィルタリングし、実際のルーティングでVueインスタンスを初期化した.しかし、この方法はログインとトップページの間でurlを介してジャンプする必要があり、常に「優雅ではない」と感じている.実際には、ログイン後に現在のインスタンスのルーティングを動的に変更できればよいが、これまでは仕方がなかったが、vue-router 2.2バージョンではrouter.addRoutes(routes)方法が追加され、動的ルーティングが実現された.
当然の実現策を考える
動的ルーティングでルーティング権限制御を実現するのは完璧な方案のようで、初期ルーティングはログインと404しかなく、ログイン後に動的に利用可能なルーティングを追加し、同時にメニューデータをVuexまたはローカルに保存して動的メニューを実現するために、キーノードは大体以下の通りである.
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
//    :
[{
  path: '/login',
  name: 'login',
  component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
  path: '/404',
  name: '404',
  component: (resolve) => require(['../views/common/404.vue'], resolve)
}, {
  path: '*',
  redirect: '/404'
}]

//    
let vm = this;
axios.get('/login', vm.user).then((res) => {
    let extendsRoutes = filterRoutes(res.menus); 
    
    //   
    sessionStorage.setItem('menus',JSON.stringify(extendsRoutes[0].children));
    //      
    vm.$router.addRoutes(extendsRoutes);
    //       
    vm.$router.push({path:'/'});
})

//        
this.menus = JSON.parse(sessionStorage.getItem('menus')); 
//        
..

今まではすべて順調に見えたが、前方に穴があった.
ダイナミックルーティングピット
最初のピットは、この論理を実装すると、サービスを開始するとデフォルトでトップページ'/'が開かれるが、初期ルーティングにはこのパスがないため、ルーティングルールに従って404にジャンプしたアプリケーションを開く最初のページが404であることがわかります.結果はもちろん'/login'にジャンプすることを望んでいるので、このような状況を判断する必要があります.ユーザーがログインする前にすべての要求が'/login'を指します.この判断はbeforeフックでもルートコンポーネントでもできます.ルートコンポーネントのcreatedコールバックで、コアコードは大体次のようになります.
1
2
3
4
let isLogin = sessionStorage.getItem('user');
if(!isLogin){
    return this.$router.push({path:'/login'});
}

このときすでに順調にログインでき、ログイン後すぐに2番目のピットが発見され、手動でページをリフレッシュすると404にジャンプする.これはリフレッシュによってVueが再インスタンス化され、ルーティングも初期ルーティングに復元されるため、現在のパスは404にリダイレクトされ、この問題の根源は利用可能なルーティングが持続化されていないことである.ルーティングデータをsessionStorageに格納することで解決でき、インスタンス化前にローカルルーティングが検出された場合、ルーティングを直接マージすることができます.
1
2
3
4
5
6
7
8
9
10
11
//      
let localRoutes = sessionStorage.getItem('routes');
if(localRoutes){
    router.addRoutes(JSON.parse(localRoutes));
}
//   
new Vue({
  el: '#app',
  router,
  render: h => h(App)
});

理論的には可能であるが、実際の動作は上記のコードよりはるかに複雑である.ローカルに存在するのは、実際のルーティングではなく権限データのみであるため、ルーティングは、保存、取得の前に権限マッチングに基づいて取得されなければならない.プロセスは煩雑であり、sessionStorageという永続的なストレージに依存しなければならない.他の方法はない.問題は、このセッションストアでは、原則として権限がメモリ変数にしか流れず、ユーザーが操作できる場所に直接露出できないことです.ユーザーが手動でセッションストアの権限を変更し、ページをリフレッシュすればフロントエンドのルーティング制御を突破できると考えてみると、非常に頼りにならないことです.
改善案
ローカルに保存できない以上、リフレッシュのたびにサービス側から再取得するので、改善されたスキームはローカルにユーザーtokenを保存し、リフレッシュのたびにtokenによってサービス側からユーザー情報と権限を再取得し、ルーティングを動的に更新し、アクセス操作はログイン検出とともにルートコンポーネントのcreatedコールバックに配置することができる.どのパスにアクセスしてもこのステップが先に実行されることを確認しますが、取得権限は非同期操作であるため、その前にアプリケーションが初期化されているため、404の問題に直面します.そのため、私たちは小さな調整をして、不整合パス('*')ホップ404のパスを初期ルーティングから削除し、ルーティングを動的に更新するときにこの構成を追加するだけです.以下のようにします.
1
2
3
4
5
6
let userPath = ...//       
//     404    
this.$router.addRoutes(userPath.concat([{
  path: '*',
  redirect: '/404'
}]));

これでリフレッシュ問題が解決し、後にいくつかの小さな問題があれば簡単です.
まずメニューです.$router.options.routesでルーティングデータにアクセスしてダイナミックメニューを実現しましたが、このデータは応答式ではなく、ダイナミックルーティングの変化を追跡できません.そのため、得られたナビゲーションメニューデータをsessionStorageまたはVuexに保存してデータ共有を実現する必要があります.
リソース権限制御も大きな影響を受けており、より細かい権限制御を実現するにはカスタム権限検証命令とグローバル検証方法が必要であり、以前のスキームでは権限はVueのインスタンス化前に取得されていたので、簡単に権限を取得して検証方法を実現することができ、その後検証方法でカスタム命令を実現し、方法をグローバルにVueに混合することができます.次に、インスタンス化され、インスタンス内のすべてのコンポーネントがカスタム命令と検証方法を使用できるようになります.しかし、現在のシナリオでは、インスタンス化してから権限を取得するが、インスタンス化するまでは権限データがまったくないため、カスタム指では実現できない.権限を取得してから検証方法を実現したが、グローバルに混合することはできない.
この問題は最後にも解決されたが、解決策は徹底的に「恥をかく」ことになり、まずグローバルな方法の実現であり、直接このようにした.
1
2
3
Vue.prototype.has = function(){
    ...
}

使用方法はグローバルブレンドの方法と全く同じです.
カスタム命令の実現はもともと頭が痛い.グローバル命令はインスタンス化する前にしか実現できないからだが、その時は確かに権限がないが、検証方法がそうである以上、命令もついでに解決した.
1
2
3
4
5
6
7
8
//    
Vue.directive('has', {
  bind: function(el, binding) {
    if (!Vue.prototype.has(binding.value)) {
      el.parentNode.removeChild(el);
    }
  }
});

不思议なprototypeは不活性な効果を持っているようで、先に登录してから実现することができて、具体的な原因は私もよく分かりません.
後記
生命は止まらないで、振り回すのは止まらないで、もともとすでに放弃した考え、しごくしているうちに意外にもしごくのが上手になって、それからまた半日をかけてもとは多くの入り口のプロジェクトを単入り口に変えて、面倒なことができましたが、心の中はやっと楽になりました.