个人自学前端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>
posted @ 2021-10-07 19:33  暗鸦08  阅读(182)  评论(0)    收藏  举报