Vue路由守卫详解

路由导航过程中有若干生命周期钩子,可以在这里实现逻辑控制。

  • 全局守卫,router.js

    //路由配置
    {
      	path: '/about',
      	name: "about",
        meta: {auth: true},		//需要认证
        component: ()=>import(/*webpackChunkName: "about"*/ "./views/About.vue")
    }
    
    //全局守卫
    router.beforeEach((to,from,next)=>{
      //要访问/about 且未登录需要去登录
      if(to.meta.auth && !window.isLogin){
        if(window.confirm('请登录')){ //用户操作了登录
          window.isLogin = true;
          next(); 	//登录成功,继续
        }else{
          next('/');	//放弃登录,回首页
        }
      }else{
        next();	//不需登录,继续
      }
    })
    

    token放在本地安全吗?有什么隐患?

  • 路由独享守卫

    beforeEnter(to,from,next){
    	//路由内部知道自己需要认证
      if(!window.isLogin){
        //...
      }else{
        next();
      }
    }
    
  • 组件内的守卫

    export default{
    	beforeRouteEnter(to,from,next){
          // 在渲染该组件的对应路由被 confirm 前调用
          // 不!能!获取组件实例 `this`
         // 因为当守卫执行前,组件实例还没被创建
        },
        beforeRouteUpdate(to,from,next){
          // 在当前路由改变,但是该组件被复用时调用
          // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
          // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
          // 可以访问组件实例 `this`
        },
        beforeRouteLeave(to,from,next){
          // 导航离开该组件的对应路由时调用
          // 可以访问组件实例 `this`
        }
        
    }
    

组件内的守卫项目应用场景:

项目中某一课程列表中的推荐课程,每次调接口都会重新返回一批不同的课程,进入课程列表看到的课程,在点击进入下级页面再回来,希望能看到刚刚进来时的数据,所以只要是页面返回当前页,需判断不重新调推荐课程接口。

export default{
	name:'learnProgram',
  data(){
    return {
      
    }
  },
  mounted(){
    
  },
  methods:{
    getRecommendCourse(){
      let params = {
        userName:this.userName || ''
      }
      this.$ajax({
        type:'get',
        url:'/course/getCourseByRandom',
        data:params
      }).then((res)=>{
        if(res.data.items.length != 0){
           this.recomendList = res.data.,items
           this.$store.commit('keeplearnPrgClass.recommendCourse',this.recomendList)
        }
      })
    },
    setData(){
      //存到vuex中,返回时从vuex中取
      this.$store.commit('keeplearnPrgClass.recommendCourse',[])
      this.getRecommendCourse()
    },
    setLocalData(){
      //返回时从vuex中取
      this.recomendList = this.$store.state.keeplearnPrgClass.recommendCourse
    }
  },
  beforeRouteEnter(to,from,next){
    if(from.name == 'myMap' || from.name == null){
      next(vm=>vm.setData())
    }else{
      next(vm=>vm.setLocalData)
    }
  },
}

Vue-router拓展

动态路由

利用$router.addRoutes()可以实现动态路由添加,常用于用户权限控制。

//router.js
//返回数据可能是这样的
//[{
//  path: "/",
//  name: 'home',
//  component: 'Home'
//}]

//异步获取路由
api.getRoutes().then(routes => {
  const routeConfig = routes.map(route => mapComponent(route));
  router.addRoutes(routeConfig);
})

//映射关系
const compMap = {
  'Home': () => import('./views/Home.vue')
}

//递归替换
function mapComponent(route){
  route.component = compMap[route.component];
  if(route.children){
    route.children = route.children.map(child => mapComponent(child))
  }
  return route
}

面包屑

利用$route.matched可得到路由匹配数组 ,按顺序解析可得路由层次关系。

//Breadcrumb.vue
watch: {
	$route(){
		//[{name:'home'},{name:'list'}]
		console.log(this.$route.matched);
    //['home','list']
    this.crumbData = this.$toute.matched.map(m => m.name)
	}
}

Vue-router 源码实现

通常用法

//krouter.js
import Home from './views/Home';
import About from './views/About';

Vue.use(VueRouter);
export default new VueRouter({
  routes: [
    {path: '/',component: Home},
    {path: '/about', component: Abbout}
  ]
})

//main.js
import router from './krouter'

分析一下需要完成的任务:

  1. 要能解析routes配置,变成一个key为path,value为component的map
  2. 要能监控url变化事件,把最新的hash值不保存到current路由
  3. 要定义两个全局组件:router-view用于显示匹配组件内容,router-link用于修改hash
  4. current应该是响应式的,这样可以触发router-view的重新渲染

具体实现

创建krouter.js

let Vue;
class VueRouter{
  constructor(options){
    this.$options = options;
    //创建一个路由path和route映射
    this.routeMap = {};
    
    //将来当前路径current需要响应式
    //利用Vue响应式原理可以做到这一点
    this.map = new Vue({
      data: {
        current: '/'
      }
    })
  }
  
  init(){
    //绑定浏览器事件
    this.bindEvents();
    //解析路由配置
    this.createRouteMap(this.$options)
    //创建router-link和
    this.initComponent()
  }
  bindEvents(){
    window.addEventListener('hashchange',this.onHashchange.bind(this))
    window.addEventListener('load',this.onHashchange.bind(this))
  }
  onHashchange(e){
    // http://localhost/#/home
    this.app.current = window.location.hash.slice(1) || '/'
  }
  createRouteMap(options){
    options.routes.forEach(item => {
      // ['/home']: {path: '/home',component: Home}
      this.routeMap[item.path] = item;
    })
  }
  initComponent(){
    Vue.component('router-link',{
      props: {
        to: String
      },
      render(h){
        //目标是:<a :href="to">
        return h('a', {attrs:{href:'#'+this.to}}, this.$slots.default)
       	//jsx 写法
        // return <a href={this.to}>{this.$slots.default}</a>
      }
    })
    
    //hash -> current -> render
    Vue.component('router-view',{
      //箭头函数能保留this指向,这里指向VueRouter实例
      render: (h) => {
        const Comp = this.routeMap[this.app.current].component;
        return h(Comp);
      }
    })
  }
}

//把VueRouter 变为插件
VueRouter.install = function(_Vue){
  Vue = _Vue; //这里保存,上面使用
  
  //混入任务
  Vue.mixin({  //混入:就是扩展Vue
    beforeCreate(){
      //这里的代码将来会在外面初始化的时候被调用
      //这样我们就实现了Vue扩展
      // this 是谁? Vue的组件实例
      //但是这里只希望跟组件执行一次,只有跟组件有 $options.router
      if(this.$options.router){
        Vue.prototype.$touter = this.$options.router;
        this.$options.router.init();
      }
      
    }
  })
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值