返回

Vue.js 面包屑导航组件问题详解与解决方案

javascript

Vue.js 面包屑导航组件的常见问题及解决方案

在使用 Vue.js 开发项目时,面包屑导航组件能清晰地展示当前页面在整个网站结构中的位置。 经常遇到一些小问题,比如:">" 分隔符的样式控制,首页">"的显示隐藏, 下面具体聊一下。

问题分析

你遇到的问题集中在以下几个方面:

  1. 最后一个面包屑元素(也就是当前页面)后面的 ">" 分隔符,鼠标悬停时出现了下划线。
  2. 鼠标悬停在 ">" 分隔符上时,光标变成了指针形状 (pointer)。
  3. 当只显示首页 "Startseite" 时,后面仍然显示了 ">" 分隔符。
  4. 当鼠标悬停在"Startseite"时,图标和文字之间的部分会带有下划线。

这些问题主要是由于 CSS 样式控制不精确和 Vue.js 条件渲染不够完善造成的。

解决方案

1. 移除最后一个面包屑元素 ">" 分隔符的下划线

原理:

问题出在你的 CSS 选择器 breadcrumb router-link:last-child::afterbreadcrumb router-link a:last-child::after 上, 以及和 router-link 默认行为的冲突. router-linkhover会激活内部a标签的默认下划线, 我们要避免::after被包含到a标签的作用范围里面。

解决办法:

  1. router-link 生成的链接和分隔符 > 进行分离. 通过给每个 router-link 外层包裹一个 span, 避免 ::after伪元素生成的>影响router-link.
  2. 直接对last-child不设置after 即可.

代码示例 (修改 breadcrumb.vue 的 template 部分):

<template>
<nav class="breadcrumb">
    <span v-for="(crumb, index) in breadcrumbs" :key="index" class="breadcrumb-item">
      <router-link :to="crumb.path">
        <template v-if="crumb.name === 'Startseite'">
          <img :src="homeIcon" alt="Home" class="breadcrumb-icon" />
          {{ crumb.name }}
        </template>
        <template v-else>
          {{ crumb.name }}
        </template>
      </router-link>
       <span v-if="index < breadcrumbs.length - 1" class="separator"> &gt; </span>
    </span>
  </nav>
</template>

<script>
import homeIcon from '@/assets/img/Home.svg'; // Pfad zur SVG-Datei

export default {
  data() {
    return {
      homeIcon,
    };
  },
  computed: {
    breadcrumbs() {
      let pathArray = this.$route.path.split("/");
      pathArray.shift(); // Remove the first empty element
      let breadcrumbArray = [{ name: "Startseite", path: "/" }];
      pathArray.forEach((path, index) => {
        breadcrumbArray.push({
          name: path.charAt(0).toUpperCase() + path.slice(1),
          path: "/" + pathArray.slice(0, index + 1).join("/"),
        });
      });
      return breadcrumbArray;
    },
  },
};
</script>

<style scoped>

.breadcrumb-icon {
  width: 1.4rem;
  height: 1.4rem;
  
}
nav a.router-link-exact-active {
  color: black;
}
.separator{
    pointer-events: none;
}

</style>

代码示例 (修改你的 component.css):

/* component.css */

.breadcrumb {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 1rem;
  color: #000000;
  position: absolute;
  width: auto;
  height: auto;
  left: 2.5rem;
  top: 6rem;
}

.breadcrumb a {
    text-decoration: none;
    color: #000000;
    padding: 0.2rem;
  
  }
.breadcrumb a:hover {
    text-decoration: underline;
     /*background-color: #ff000000;*//* unnecessary since text-decoration: underline takes precedence.*/
  }

.breadcrumb-icon {
    width: 1.4rem;
    height: 1.4rem;
    margin-right: 0.2rem;
    position: relative;
    top: 0.25rem; /* Adjust this value to move the image up or down */
}

/* important to prevent events for seperators */
.separator{
    pointer-events: none;
}

2. 移除 ">" 分隔符上的指针光标

原理:

">" 分隔符是 CSS 用 ::after 伪元素添加的,要改变鼠标悬停时的样式,就要操作这个伪元素。但为了使 ::after 伪元素上的样式可被配置,需要使::after与元素本体拥有一个可以独立调整样式class。所以最好使用上述方案中的template模版:一个单独的 span 标签来包裹">", 而不是用after伪元素.

解决办法(沿用上面的例子):

上面的.separator类已经包含了这一步需要的代码. pointer-events: none;让 ">" 所在的 span 不响应任何鼠标事件,鼠标样式自然也不会改变了。

3. 移除首页的 ">" 分隔符

原理:

breadcrumbs 计算属性总是会生成一个至少包含首页的数组。 我们只要利用v-if, 当数组只有首页时, 不渲染分隔符就可以.

解决办法:(已经在上述代码示例中实现)
代码中v-if="index < breadcrumbs.length - 1" 确保了只有当index 小于最大值时才渲染">", 这避免了只有首页时的尾部 ">".

4.移除"Startseite"图标与文字之间的下划线.

原理 : 依然是 router-link 下的a标签的hover事件, 会使得文字拥有下划线. 我们将img标签也放到router-link内, 它自动继承router-linkhover下划线. 我们通过css避免文字被修饰, 图标自然不会再有错误位置的下划线了.

修改 .vue 文件中模版:

<template>
<nav class="breadcrumb">
    <span v-for="(crumb, index) in breadcrumbs" :key="index" class="breadcrumb-item">
      <router-link :to="crumb.path">
        <template v-if="crumb.name === 'Startseite'">
          <img :src="homeIcon" alt="Home" class="breadcrumb-icon" />
          <span class="home-text">{{ crumb.name }}</span>
        </template>
        <template v-else>
          {{ crumb.name }}
        </template>
      </router-link>
       <span v-if="index < breadcrumbs.length - 1" class="separator"> &gt; </span>
    </span>
  </nav>
</template>

<script>
import homeIcon from '@/assets/img/Home.svg'; // Pfad zur SVG-Datei

export default {
  data() {
    return {
      homeIcon,
    };
  },
  computed: {
    breadcrumbs() {
      let pathArray = this.$route.path.split("/");
      pathArray.shift(); // Remove the first empty element
      let breadcrumbArray = [{ name: "Startseite", path: "/" }];
      pathArray.forEach((path, index) => {
        breadcrumbArray.push({
          name: path.charAt(0).toUpperCase() + path.slice(1),
          path: "/" + pathArray.slice(0, index + 1).join("/"),
        });
      });
      return breadcrumbArray;
    },
  },
};
</script>

并在CSS文件中加入:

.home-text {
     text-decoration: none;
}
.breadcrumb a:hover .home-text{
    text-decoration: underline;
}

完整代码示例(Breadcrumb.vue)

整合后的 Breadcrumb.vue 组件代码:

<template>
  <nav class="breadcrumb">
    <span v-for="(crumb, index) in breadcrumbs" :key="index" class="breadcrumb-item">
      <router-link :to="crumb.path">
        <template v-if="crumb.name === 'Startseite'">
          <img :src="homeIcon" alt="Home" class="breadcrumb-icon" />
          <span class="home-text">{{ crumb.name }}</span>
        </template>
        <template v-else>
          {{ crumb.name }}
        </template>
      </router-link>
       <span v-if="index < breadcrumbs.length - 1" class="separator"> &gt; </span>
    </span>
  </nav>
</template>

<script>
import homeIcon from '@/assets/img/Home.svg';

export default {
  data() {
    return {
      homeIcon,
    };
  },
  computed: {
    breadcrumbs() {
      let pathArray = this.$route.path.split("/");
      pathArray.shift();
      let breadcrumbArray = [{ name: "Startseite", path: "/" }];
      pathArray.forEach((path, index) => {
        breadcrumbArray.push({
          name: path.charAt(0).toUpperCase() + path.slice(1),
          path: "/" + pathArray.slice(0, index + 1).join("/"),
        });
      });
      return breadcrumbArray;
    },
  },
};
</script>

<style scoped>
.breadcrumb {
  display: flex;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 1rem;
  color: #000000;
  position: absolute;
  width: auto;
  height: auto;
  left: 2.5rem;
  top: 6rem;
}

.breadcrumb a {
  text-decoration: none;
  color: #000000;
  padding: 0.2rem;
}

.breadcrumb a:hover {
  text-decoration: underline;
}
 .breadcrumb a:hover .home-text{
    text-decoration: underline;
}
.breadcrumb-icon {
  width: 1.4rem;
  height: 1.4rem;
  margin-right: 0.2rem;
  position: relative;
  top: 0.25rem;
}
.separator {
    pointer-events: none;/*very important!*/
}

.home-text{
    text-decoration: none;
}
nav a.router-link-exact-active {
  color: black;
}
</style>

这样,通过将router-link和其他元素例如">"分隔符隔离开,利用css控制显示, 利用v-if进行条件判断,就解决了所有提出的问题。