Vue中同一个路由的跳转及动画

在重构vue trailer项目的时候,遇到过一个问题,在电影的详情页面上有一个同类电影的按钮,里面有最多 5 部的同类电影,可以通过点击跳转到相应的电影详情页中去。 比如:在电影 巨齿鲨 的详情页面中,有 解码者 等 5 部同类的电影,点

目录
  1. 同一个路由的跳转
  2. 同一个路由的切换的动画

同一个路由的跳转#

在重构vue-trailer项目的时候,遇到过一个问题,在电影的详情页面上有一个同类电影的按钮,里面有最多 5 部的同类电影,可以通过点击跳转到相应的电影详情页中去。

比如:在电影巨齿鲨的详情页面中,有解码者等 5 部同类的电影,点击同类电影解码者区域就可以跳转到电影解码者的详情页面。

所有的电影的详情页都是同一个组件movie-detail.vue,使用的也是动态路由{path: '/detail/:id'},根据电影的不同 id,在组件生成时请求不同的电影数据,然后渲染到页面上。从主页的详情按钮点进去,是没有一点问题的。但是,在我点击同类电影,切换其他电影的详情页面时,除了地址栏上 url 的 id 变化之外,页面是一点反应都没有的。

首先当然是翻看vue-router的文档了,发现在导航守卫下面的组件守卫beforeRouteUpdate的代码注释有提到过:带动态参数的路径之间的跳转可能会用到这个函数,因为 vue 渲染同样的组件,所以组件实例被复用了,然后可能会到这个钩子,看了后一脸的懵比。

然后只好去万能google上找了,搜索了关键字vue同路由页面的跳转之后,发现在vue-router的仓库的 issue 中有人提出了这个问题,然后打开了页面看了下,发现是 16 年的 issue 的,而且还是针对vue1.0的提的 issue,不过大概看了下,发现有 vue 作者的回答,他建议是重新载入数据,也有其他人说是使用canReuse,但是这个这个 api 在 2.0 的时候已经删除掉了,后面只需往下看,也有人建议使用beforeRouteUpdate,但是没有说具体怎么操作,有点纠结。

后面又重新研究了路由守卫和beforeRouteUpdate的用法之后,再根据之前 vue 作者建议的重新载入数据之后,终于明白了要怎么做了。

beforeRouteUpdate(to, from, next) {
  this.id = to.params.id  // 获得跳转电影页面的id
  this.getMovieDetail(this.id)  // 通过id获取数据
  this.setPlayer() // 重置播放器
  next()
}

原理是:使用beforeRouteUpdate钩子,在路由更新前获取跳转页面电影的数据,然后 vue 会把新的数据渲染到页面上。这样的话,页面应该就会更新了。

按照上面的代码,点击同类电影跳转到同类电影的详情页,页面刷新了,问题就解决了。之后,在电影的列表页面也是使用这种方法切换页面,根据不同的类型或者年份来请求不同的电影数据。

beforeRouteUpdate(to, from, next) {
  this.category = to.params.category
  this.year = to.params.year
  this.getMovies({
    type: this.category === '全部' ? null : this.category,
    year: this.year
  })
  next()
}

同一个路由的切换的动画#

页面渲染完成了,然后想添加一些过渡动画增加效果。又遇到了类似的问题,添加过渡动画无效。

<transition
  appear
  mode="out-in"
  enter-active-class="animated slideInLeft"
  leave-active-class="animated slideOutRight"
>
  <router-view />
</transition>

官方文档有说明这个问题:

当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在transition组件中的多个元素设置 key 是一个更好的实践。

然后修改代码,设置 key 值:

<transition
  appear
  mode="out-in"
  enter-active-class="animated slideInLeft"
  leave-active-class="animated slideOutRight"
>
  <router-view :key="key" />
</transition>
<script>
  // ...
  export default {
    computed: {
      key() {
        return this.$route.path
    },
  }
</script>

router-view标签绑定一个 key 属性,值为不相同的字符串就可以了,这里可以使用 router 的path,因为它们都不一样。这样 vue 就可以根据 key 值来区分他们,动画效果也就出来了。