1. 程式人生 > 其它 >個人自學前端33-Vue10-路由守衛

個人自學前端33-Vue10-路由守衛

目錄

一. 路由守衛

路由守衛一定會用到的api
路由守衛也叫路由鉤子,路由生命週期 => 路由守衛其實都是在路由跳轉的特定階段觸發的函式。

路由跳轉流程。

  1. 路由觸發,準備跳轉
  2. 觸發Home元件的一個鉤子函式,beforeRouteLeave (路由離開前)
  3. 呼叫全域性的 beforeEach(所有路由跳轉都會觸發的一個鉤子) 鉤子
  4. 在重用的元件(動態路由元件)裡呼叫 beforeRouteUpdate.(當前路由元件更新時)
  5. 在路由配置裡呼叫 beforeEnter(路由選項獨享守衛)
  6. 解析非同步路由(路由懶載入)元件。
  7. 在News元件裡呼叫 beforeRouteEnter (當前路由準備進入時)。
  8. 呼叫全域性的 beforeResolve (完成跳轉前)。
  9. 導航被確認。(完成路由跳轉)
  10. 呼叫全域性的 afterEach 鉤子。
  11. 觸發 DOM 更新(更新路由元件檢視)
  12. 呼叫 beforeRouteEnter 守衛中傳給 next 的回撥函式,建立好的元件例項會作為回撥函式的引數傳入。

守衛分類:

  1. 全域性守衛
    beforeEach (很常用)
    beforeResolve
    afterEach
  2. 路由獨享守衛
    beforeEnter
  3. 元件獨享守衛
    beforeRouteEnter (常用)
    beforeRouteUdpate (常用)
    beforeRouteLeave (常用)

1. 路由守衛的引數

  1. to 目標路由的$route物件
  2. from 起始路由的$route物件
  3. next 回撥函式,用於攔截路由或者路由傳參
    next的引數:
    • 沒引數 => 正常跳轉
    • false => 不讓跳轉
    • 字串路徑 => 重定向
    • 物件 => 重定向時路由傳參.
    • 函式 => 跳轉的最後節點觸發,引數是當前的元件例項.
    const Home = {
      template: `
        <div>
          <h3>首頁</h3>
        </div>
      `,
      // Home路由跳轉到其他路由前觸發的鉤子守衛.
      beforeRouteLeave(to, from, next) {
        // console.log('準備離開Home');

        // 通過to物件,可以知道目標路由是哪個
        console.log('to', to.path);
        // 通過from物件,可以知道起始路由是哪個.
        console.log('from', from.path);
        // next是一個回撥函式
        console.log('next', next);
        // 手動呼叫next才能正常跳轉路由(一定不要忘記)
        next();

        // 引數寫false就是跳轉失敗
        // next(false);
      }
    }

    const News = {
      template: `
        <div>
          <h3>新聞</h3>
        </div>
      `,
      data() {
        return { msg: 'News元件' }
      },
      // 元件進入前的守衛.(別的路由進入News路由時觸發)
      // beforeRouteEnter鉤子內部能使用this.
      // 這裡的this不指向當前的News元件.而是window
      // beforeRouteEnter觸發時,元件還沒有建立
      beforeRouteEnter(to, from, next) {
        // undefined
        console.log('msg',this.msg);

        // next的引數可以是一個回撥函式,在整個路由跳轉的最後階段觸發
        // 這個回撥函式的引數就是當前的News元件例項.
        // next((vm) => {
        //   console.log('msg', vm.msg)
        // });

        // 進入News前,重定向到sport。
        // next('/sport');

        // 進入News前,重定向到sport,並且傳參msg給sport元件
        next({ path: '/sport', query: { msg: 10000 } });
      }
    }
  </script>

2.元件獨享守衛

beforeRouteLeave => 守衛(保安),可以讓路由跳轉被攔截 => 路由跳轉到其他路由前觸發的鉤子守衛.
beforeRouteEnter => 元件進入前的守衛.

beforeRouteLeave和beforeRouteEnter只能在路由元件中觸發.
在路由元件(routes中配置的元件)的子元件中是不觸發的.

動態路由間的切換時不會觸發beforeRouteLeave和beforeRouteEnter.
動態路由切換,可以通過beforeRouteUpdate來監測.
同樣的.beforeRouteUpdate在路由元件的子元件中也不觸發.

    const box = {
      template: `
        <h3>box子元件</h3>
      `,
      // 這裡的leave的元件是不觸發的
      beforeRouteUpdate(to, from, next) {
        console.log('box的Update')
        next();
      }
    }
    const Home = {
      template: `
        <div>
          <h3>首頁--{{$route.path}}</h3>
          <box />
        </div>
      `,
      components: { box },
      // 動態路由中不觸發beforeRouteLeave
      beforeRouteLeave(to, from, next) {
        console.log('Home的leave')
        next();
      },
      // 動態路由切換時觸發.
      beforeRouteUpdate(to, from, next) {
        console.log(from.params.path + '到' + to.params.path);
        next();
      }
    }

從非動態路由跳轉到動態路由,會觸發動態路由內的Enter.
從動態路由跳轉到非動態路由,會觸發動態路由內的Leave.

3. 全域性守衛

全域性守衛,所有的路由跳轉都會觸發這個鉤子.
beforeEach相對於是全域性的beforeRouteEnter(沒有全域性的Leave守衛)

    router.beforeEach((to, from, next) => {
      console.log('進入' + to.path + '元件');
      next();
    });

例子:簡易登入的守衛邏輯

  <script>
    // 一個App應用
    // 1:有寫頁面需要登入,有些頁面不需要登入.
    // 2:跳轉到需要登入的頁面時,先檢測你是不是已經登入了,如果沒有登入直接跳轉到登入頁.,如果登入了,直接跳轉.
    // 預設沒有登入.
    let isLogin = false;
    const Login = {
      template: `
        <div>
          <input type='text' />
          <button @click='toPage'>登入</button>
        </div>
      `,
      props: ['name'],
      methods: {
        toPage() {
          // 切換登入狀態
          isLogin = true;
          // 跳轉到指定路由.
          this.$router.push({ name: this.name })
        }
      }
    }
    const Home = {
      template: `
        <div>
          <slot />
          <h3>首頁</h3>
        </div>
      `,
    }
    const News = {
      template: `
        <div>
          <slot />
          <h3>新聞</h3>
        </div>
      `,
    }
    const Sport = {
      template: `
        <div>
          <slot />
          <h3>體育</h3>
        </div>
      `,
    }
    // 例項化路由
    const router = new VueRouter({
      // 路由元件和路由路徑的對應關係 => 路由選項
      routes: [
        {
          path: '/home',
          component: Home,
          name: 'home',
          meta: {
            // 不需要登入
            authLogin: false
          }
        }, {
          path: '/news',
          component: News,
          name: 'news',
          meta: {
            authLogin: true
          }
        }, {
          path: '/sport',
          component: Sport,
          name: 'sport',
          meta: {
            authLogin: true
          }
        }, {
          path: '/login',
          component: Login,
          name: 'login',
          meta: {
            authLogin: false
          },
          props: true
        },{
          path: '/',
          redirect: '/home'
        }
      ]
    });
    // 全域性守衛處理 登入邏輯
    router.beforeEach((to, from, next) => {
      if (to.meta.authLogin) {
        if (!isLogin) {
          // 重定向,並且傳遞目標路由的name
          next({ name: 'login', params: { name: to.name }})
        } else {
          next()
        }
      } else {
        next()
      }
    });
    const App = {
      template: `
        <div>
          <router-view>
            <router-link to='/home'>首頁</router-link>
            <router-link to='/news'>新聞</router-link>
            <router-link to='/sport'>體育</router-link>
          </router-view>
        </div>
      `
    };
    new Vue({
      render: h => h(App),
      // 掛載路由
      router
    }).$mount('#app');
  </script>

4.路由獨享守衛

    const router = new VueRouter({
      routes: [
        {
          path: '/home',
          component: Home
        }, {
          path: '/news',
          component: News,
          components: { Home, box },
          // 對於選項的components有意義
          // News選項獨享的守衛,寫在這裡和寫在元件中的效果一致.
          // 這裡沒辦法通過next的回撥獲取元件例項.
          beforeEnter(to, from, next) {
            console.log('Enter');
            next();
          }
        }, {
          path: '/sport',
          component: Sport
        }, {
          path: '/',
          redirect: '/home'
        }
      ]
    })

5.如何在路由跳轉時實現一些額外的邏輯?

如何監聽路由的跳轉?

  1. 不快取元件的情況
    • created和mounted中監聽.
    • watch監聽$route變化(任意元件中都可以使用)
    • 路由守衛監聽.(只能在路由元件中使用)
  2. 快取的元件
    • watch監聽$route變化
    • ativated和deactivated.(路由元件和路由元件的子元件都可以觸發)
    • 路由守衛監聽.(只能在路由元件中使用)
  3. 動態路由
    • watch監聽$route變化
    • beforeRouteUpdate守衛監聽

watch監聽例子:

  <script>
    const Home = {
      template: `
        <div>
          <h3>首頁</h3>
        </div>
      `,
      created() {
        console.log('Home元件切換11');
      },
      watch: {
        // 每次路由發生跳轉,路徑都會改變,路徑改變$route內的屬性就會變,從而觸發watch
        '$route': {
          immediate: true,
          handler() {
            console.log('Home元件切換22');
          }
        }
      },
      beforeRouteEnter(to, from, next) {
        console.log('Home元件切換33');
        next();
      }
    }
    const News = {
      template: `
        <div>
          <h3>新聞</h3>
        </div>
      `,
      data() {
        return { msg: 'News元件' }
      }
    }
    const Sport = {
      template: `
        <div>
          <h3>體育</h3>
        </div>
      `
    }
    const router = new VueRouter({
      routes: [
        {
          path: '/home',
          component: Home
        }, {
          path: '/news',
          component: News
        }, {
          path: '/sport',
          component: Sport
        }, {
          path: '/',
          redirect: '/home'
        }
      ]
    })
    const App = {
      template: `
        <div>
          <router-link to='/home'>首頁</router-link>
          <router-link to='/news'>新聞</router-link>
          <router-link to='/sport'>體育</router-link>
          <router-view />
        </div>
      `,
      watch: {
        $route: {
          immediate: true,
          handler() {
            console.log('路由切換了')
          }
        }
      }
    };
    new Vue({
      render: h => h(App),
      // 掛載路由
      router
    }).$mount('#app');
  </script>