当前位置: 首页 > article >正文

vue router路由复用及刷新问题研究

路由复用问题

当路由匹配路径未发生变化时,只是相关的参数发生了变化,路由跳转时,会发现虽然地址栏中的地址更新到了新的链接,但是页面渲染并未触发响应路由组件的created,mounted等钩子函数,也就意味着组件并没有被重新创建,也就是路由复用问题。
在官方文档中,下面这段话同样体现了这个问题。
在这里插入图片描述
在这里插入图片描述
了解vue组件复用的应该知道,复用时并不会更新组件内部自身data中拥有的响应式属性,但是如果是通过props传递过来的属性,在复用组件时,进行diff patch时,是会更新的。具体组件复用更新原理不清楚的,可以参考博客组件复用

vue-router在匹配到对应的路由后,同样会更新对应组件的props参数,相关实现逻辑在router-view组件的render函数中有体现,其中fillPropsinData将路由表中的props参数传递给路由组件的props

var configProps = matched.props && matched.props[name];
    // save route and configProps in cache
    if (configProps) {
      extend(cache[name], {
        route: route,
        configProps: configProps
      });
      fillPropsinData(component, data, route, configProps);
    }

具体怎么设置props呢?可以参考官方文档 路由组件传参

props参数复用更新

下面是一个简单的 detail.vue 示例,用于显示详情页面。假设我们通过路由参数 id 来获取并显示特定项的详细信息。

首先,确保你的路由配置中包含了 detail 路由,并且能够传递 id 参数。例如:

// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import List from '@/page/list.vue';
import Detail from '@/page/detail.vue';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/list',
      name: 'List',
      component: List
    },
    {
      path: '/detail/:id',
      name: 'Detail',
      component: Detail
    }
  ]
});

下面是一个简单的 list.vue 示例,其中包含一个列表,列表项可以跳转到详情页面。假设详情页面的路由为 /detail/:id。

<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item.id" @click="goToDetail(item.id)">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { id: 1, name: 'Item 1' },
        { id: 2, name: 'Item 2' },
        { id: 3, name: 'Item 3' },
        // 更多列表项...
      ]
    };
  },
  methods: {
    goToDetail(id) {
      this.$router.push({ path: `/detail/${id}` });
    }
  }
};
</script>

<style scoped>
ul {
  list-style-type: none;
  padding: 0;
}

li {
  padding: 8px;
  cursor: pointer;
  border-bottom: 1px solid #ccc;
}

li:hover {
  background-color: #f0f0f0;
}
</style>

接下来是 detail.vue 的代码:

<template>
  <div>
    <h1>Detail Page - {{id}} </h1>
    <div v-if="item">
      <h2>{{ item.name }}</h2>
      <p>ID: {{ item.id }}</p>
      <p>Description: {{ item.description }}</p>
    </div>
    <div v-else>
      <p>Loading...</p>
    </div>
    <button @click="goBack">Back to List</button>
    <button @click="goToDetail">Go to other detail 2</button>
  </div>
</template>

<script>
export default {
  props: ['id'],
  data() {
    return {
      item: null
    };
  },
  created() {
    this.fetchItem();
  },
  methods: {
    fetchItem() {
      const id = this.$route.params.id;
      // 假设我们有一个方法来获取数据,这里用模拟数据代替
      const items = [
        { id: 1, name: 'Item 1', description: 'Description for Item 1' },
        { id: 2, name: 'Item 2', description: 'Description for Item 2' },
        { id: 3, name: 'Item 3', description: 'Description for Item 3' }
      ];
      this.item = items.find(item => item.id === parseInt(id));
    },
    goBack() {
      this.$router.push({ name: 'List' });
    },
    goToDetail() {
        this.$router.replace({ name: 'Detail', params: { id: 2 } })
    }
  }
};
</script>

<style scoped>
h1 {
  font-size: 24px;
  margin-bottom: 20px;
}

h2 {
  font-size: 20px;
  margin-bottom: 10px;
}

p {
  margin: 5px 0;
}

button {
  margin-top: 20px;
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}
</style>

在这里插入图片描述
从列表跳转到详情页时,可以正常的更新渲染,但是可以到详情页之间互相跳转时,页面的渲染是有问题的。以上面为例,从detail page 3跳转到 detail page 2时,只更新了title 中的id,而对应的内容并没有更新。
在这里插入图片描述
从detail.vue的template中可以看到,组件中props中的id对应的渲染更新了,而item作为组件自身内部响应式并没有更新。因为item的赋值逻辑是在created里触发的,而详情页跳转详情页的情况,是不会触发created的。

渲染更新

如果想让详情页之间的跳转能够及时地更新,要如何做呢?也就是最初上面官网上介绍的,如何响应路由参数的变化?官方给出的方案有相中:

路由守卫

虽然相同路由之间跳转,不会触发created等钩子函数,但是会触发路由守卫函数beforeRouteUpdate,可以考虑将原先的数据初始化逻辑放到这里。以上面的例子为例,可以在代码中加入下面的代码来触发页面的更新:

beforeRouteUpdate (to, from, next) {
    console.log('=====beforeRouteUpdate====',to.params.id, this.id)
    this.fetchItem(to.params.id)
    next()
}
//这里id改为传入的,以便上面方法中能够传入最新的路由参数
fetchItem(id) {
      // 假设我们有一个方法来获取数据,这里用模拟数据代替
      const items = [
        { id: 1, name: 'Item 1', description: 'Description for Item 1' },
        { id: 2, name: 'Item 2', description: 'Description for Item 2' },
        { id: 3, name: 'Item 3', description: 'Description for Item 3' }
      ];
      this.item = items.find(item => item.id === parseInt(id));
    }

在这里插入图片描述
这里需要注意使用的是beforeRouteUpdate,而不是beforeRouteEnter,经实际验证,beforeRouteEnter只在从其他路由进入该路由时触发,而在像详情页跳转详情页这样的路由复用时,不会触发。

watch监听路由变化

在这里插入图片描述
官网给出的解决方法,除了路由守卫,就是监听$route,加入下面的代码,同样可以达到对应的效果。

watch: {
    '$route' (to, from) {
      console.log('=====watch====',to.params.id, this.id)
      this.fetchItem(to.params.id)
    }
  },

打破路由复用

上面的两种方法虽然都能达到路由试图随参数变化而更新,但是本质上还是在复用的基础上,在合适的时机重新触发处理逻辑函数,已达到更新目的。由于是复用的实例,所以在更新时并不会主动销毁之前创建的资源,如果有销毁的需求,比如每次进入详情页都会创建一些canvas等,在路由参数发生变化时,需要重新绘制,通常是需要先销毁之前的,再重新绘制新的,而采用上面两种方法,在复用渲染时,需要我们手动去管理这些。
如果我们期望的是不管路由是不是从详情页跳转详情页复用,每次参数变化时,不复用之前的,销毁之前创建的组件实例,然后重新创建新的组件实例,要如何做呢?
使用 key 强制重新渲染,通过为组件设置一个唯一的 key,Vue 会认为这是一个不同的组件实例,从而强制重新渲染。

<template>
  <router-view :key="$route.fullPath"></router-view>
</template>

上面直接在route-view上绑定key值,会影响所有的路由,如果只想针对详情页路由处理,可以直接在组件上使用 key:

<!-- 这里新建一个wrapper.vue 路由组件,修改路由表里的detail路由对应的组件为这个wrapper.vue组件 -->
<template>
    <!-- Detail 组件 -->
    <Detail v-bind="$attrs" :key="$route.params.id"></Detail>
</template>

注意上面直接在路由组件上绑定key值时,需将原有Detail路由组件作为新的路由组件的子组件。因为相同的路径对应的路由还是默认会走vue-router的组件复用逻辑,如果直接在原有的Detail组件内部绑定key值是起不到作用的,这里修改新的路由组件为wrapper.vue组件,复用时也是复用的wrapper组件,在对wrapper组件进行diff算法更新时,由于key值不同,会重新触发子组件的渲染,此时就会重新触发Detail组件的created钩子函数,同时也会触发先前组件的beforeDestroy钩子函数,销毁之前的实例。


http://www.kler.cn/a/519483.html

相关文章:

  • Python从0到100(八十五):神经网络与迁移学习在猫狗分类中的应用
  • 大数据之路:阿里巴巴大数据实践(1)
  • React 前端框架实战教程
  • kafka-保姆级配置说明(consumer)
  • 【2024年华为OD机试】 (A卷,100分)- 整理扑克牌(JavaScriptJava PythonC/C++)
  • SpringBoot统一功能处理
  • 从 VJ 拥塞控制到 BBR:ACK 自时钟和 pacing
  • 《Kotlin核心编程》上篇
  • 【动态规划】杨表
  • YOLOv11改进,YOLOv11检测头融合DSConv(动态蛇形卷积),并添加小目标检测层(四头检测),适合目标检测、分割等任务
  • SQL注入漏洞之SQL注入基础知识点 如何检测是否含有sql注入漏洞
  • 【leetcode100】二叉树的层序遍历
  • Elasticsearch中的度量聚合:深度解析与实战应用
  • mock可视化生成前端代码
  • javascript-es6 (一)
  • 【Vim】Vim 中将文件内容复制到系统剪切板的方法
  • 基于Oracle 19C的ADVM与ACFS标准化实施文档
  • Python Pandas数据清洗与处理
  • RabbitMQ---面试题
  • Pyecharts图表交互功能提升
  • vue3+elementPlus之后台管理系统(从0到1)(day4-完结)
  • 在Ubuntu上安装RabbitMQ教程
  • Go语言快速开发入门
  • 微信开发者工具的快捷键
  • ray.rllib-入门实践-12:自定义policy
  • Maui学习笔记-SignalR简单介绍