十一、Vue-路由

111 阅读3分钟

没有比官方文档更全的,必读必读必读!!!


一、常见问题:

复用组件,路由反复变化了,组件created不再触发

解决方案:使用watch 监听$router

watch:{
	$router:{
  	deep:true,
        immediate: true, // 立即执行
        handler(newValue,oldValue){
    
    }
  }
}

二、进阶-路由守卫

正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

有多种机会植入路由导航过程中

1、全局的

2、单个路由独享的

3、组件级的。

1、全局

// 全局
router.beforeEach((to, from, next) => { // ...
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。
})
// 使用
router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
		if (window.isLogin) { 
      next()
		} else { 
      next('/login?redirect='+to.fullPath)
		}
	} else {
		next() 
	}
})

router.afterEach((to, from) => {
  // ...
})

1.1、使用场景:全局登录【白名单】

1、使用router:meta数据,区分是否要校验登录(配合meta创建一个白名单)

2、使用beforeEach拦截。

3、防止出现死循环(排除登录、注册页面和mete中不需要校验页面)

// router 的 meta数据区分该路由是否需要校验登录

router.beforeEach((to, from, next) => { 
	if (to.meta.auth) {
		if (window.isLogin) { 
      next()
		} else { 
      next('/login?redirect='+to.fullPath)
		}
	} else {
		next() 
  }
})

2、单个路由独享

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

这些守卫与全局前置守卫的方法参数是一样的。

3、组件内

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

注意点!!!

beforeRouteEnter 守卫 不能 访问 this ,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开, 该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

三、进阶-完整路由解析流程

step1、导航被触发。

step2、在失活的组件里调用 beforeRouteLeave 守卫。

step3、调用全局的 beforeEach 守卫。

step4、在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。

step5、在路由配置里调用 beforeEnter

step6、解析异步路由组件。(各个组件的生命周期)。

step7、在被激活的组件里调用 beforeRouteEnter

step8、调用全局的 beforeResolve 守卫 (2.5+)。

step9、导航被确认。 (各个组件的生命周期)。

step10、调用全局的 afterEach 钩子。

step11、触发 DOM 更新。

step12、调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。


四、进阶-数据获取时机

路由激活时,获取数据的时机有两个:

4.1、路由导航前

// 组件未渲染,通过给next传递回调访问组件实例 
beforeRouteEnter (to, from, next) { 
  getPost(to.params.id, post => {
	next(vm => vm.setData(post)) 
  })
},
  
  
// 组件已渲染,可以访问this直接赋值 
beforeRouteUpdate (to, from, next) {
  this.post = null 
  getPost(to.params.id, post => {
	this.setData(post)
	next() 
  })
},

4.2、路由导航后

created () { 
  this.fetchData()
}, 
  
watch: {
    '$route': 'fetchData'
}

五、进阶-动态添加-router.addRoutes()

[真正的白名单] 通过 router.addRoutes(routes) 方式动态添加路由

根据不同用户的不同权限,添加多份路由表!!!

// Login.vue用户登录成功后动态添加/about 
login() {
	window.isLogin = true;
	this.$router.addRoutes([ 
            { path: "/about"}
	]);
	const redirect = this.$route.query.redirect || "/";
	this.$router.push(redirect); 
}
const router1 = [{},{},{},...];
const router2 = [{},{},{},...];
const router3 = [{},{},{},...];
login() {
  ...
	window.isLogin = true;
	if(user.auth === A){
  	this.$router.addRoutes(router1);
  }
  if(user.auth === B){
  	this.$router.addRoutes(router2);
  }
  if(user.auth === C){
  	this.$router.addRoutes(router3);
  }
}

五、进阶-路由缓存

方法:

利用keepalive做组件缓存,保留组件状态,提高执行效率

特点:

两个特别的生命周期:activated、deactivated

坑点:activated触发 影响 子组件的生命周期触发

方法一:

// include 使用 “,”号隔开的字符串 或者数组,区分路由组件。
// max使用,设置缓存栈数量:number。

<keep-alive include="about" :max="10"> 
  <router-view></router-view>
</keep-alive>

使用include或exclude时要:《给组件设置name》不是路由name

max使用,设置缓存栈数量:number。

方法二:

使用meta数据控制

// router.js
[{
    path: '/search',
    name: 'search',
    meta: {
      title: '搜索',
      showTabbar: false,
      keepAlive: true,
      noShowBackHome: true,
      showFloatCart: true,
      hideHeader: true,
      visitor: true
    },
    component: function (resolve) {
    	require(['../components/Search'], resolve);
    },
}]


// index.vue
<keep-alive v-if="$route.meta.keepAlive">
    <router-view class="router-view" :class="{'no-header':!showHeader}"></router-view>
</keep-alive>

<router-view
    v-if="!$route.meta.keepAlive"
    class="router-view"
    :class="{'no-header':!showHeader}"
></router-view>