如何让 router-link 与自定义 link 组件一起工作?

How to make router-link work with custom link component?

<base-link>分量

我有一个 Vue 组件 <base-link>,每次我想要一个锚点时我都会使用它。它主要用于应用特定于 links 的样式,以便整个页面上的所有 links 在不应用全局样式的情况下看起来都一样。

使 <router-link> 使用 <base-link>

当使用 <router-link> 组件创建 link 时,我无法应用这些样式(<base-link> 样式是有范围的)除非 <router-link> 使用我的 <base-link>用于创建锚元素的组件。

幸运的是 <router-link> 提供了 tag attribute,它似乎正是这样做的。不幸的是我无法让它工作。我有 2 个问题:

  1. 我所有的组件都是在本地注册的(我使用 ES6 模块和 Webpack 并在每次需要时在本地导入组件)。 <router-link> 不知道 <base-link> 组件是什么,也无法渲染它。有没有办法注入本地组件供 <router-link> 使用?

  2. 为了解决问题 #1,我认为全局声明 <base-link> 组件就足够了。不幸的是它仍然不起作用。这次 <base-link> 组件可以正确呈现,但仍然无法正常工作 - 不会对点击事件做出反应。在我看来,问题是它的 href 属性根本没有设置。有没有办法让 <router-link> 正确设置? (无需手动设置)

问题

如何解决问题 #1 和 #2?我怀疑 #1 可能是不可能的,但我希望至少 #2 是。

代码示例

Here是笔下的代码,说明了这两个问题。

const router = new VueRouter({
  routes: [
    {
      path: "/",
      component: {
        template: '<p>Homepage template</p>'
      }
    },
    {
      path: "/subpage",
      component: {
        template: '<p>Subpage template</p>'
      }
    }
  ]
});

// Globally registered BaseLink.
Vue.component('BaseLinkGlobal', {
  props: {
    href: String
  },
  template: `
    <a
      :href="href"
      class="BaseLinkGlobal"
    >
      <slot />
    </a>
  `
})

const vue = new Vue({
  el: "#app",
  router,
  components: {
    // Locally registered BaseLink.
    BaseLinkLocal: {
      props: {
        href: String
      },
      template: `
        <a
          :href="href"
          class="BaseLinkLocal"
        >
          <slot />
        </a>
      `
    }
  },
  template: `
    <div>
      <!-- 2 router links. One uses locally registered BaseLink
        -- and the other one a globally registered one. -->
      <nav>
        <router-link
          to="/"
          tag="base-link-local"
        >
          Home
        </router-link>
        <router-link
          to="/subpage"
          tag="base-link-global"
        >
          Subpage
        </router-link>
      </nav>
      <router-view />
    </div>
  `
});

您可以创建一个基础 link 组件,它可以兼作普通 a 标签或 <router-link> 当您需要时。

//Base link

<template>
  <component :is="type" :class="{'base': type === 'a'}" v-bind="$attrs">
    <slot></slot>
  </component>
</template>

<script>
export default {
  props: {
    routerLink: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    type() {
      return this.routerLink ? 'router-link' : 'a'
    }
  }
}
</script>

<style scopex>
.base {
  border: 1px solid red;
}
</style>

用法

当你想把它当作普通的 link 使用时,只要不提供 router-link 道具,如下所示:

<base-link>This is a base a tag</base-link>

要将其用作 router-link,只需添加 router-link 属性和 to 属性:

<base-link router-link to="/">This is router-link</base-link>

关于base-link成分的说明:

  • 我们使用 vuejs 提供的 component 来渲染 a 标签或 router-link 基于 routerLink 属性的真实性。
  • 如果是正常的 link 即 a
  • ,则添加 .base 的 class
  • 我们绑定 $attrs,这允许我们使组件更加透明,即允许我们使用 hrefto 之类的属性,而无需将它们作为 props 传递。

    <base-link href="https://google.com">go to google</base-link>
    

你可以看看here以获得更多关于$attrs

用法的解释

这是为了解决问题#2

全局组件不继承路由器的事件监听器link。您可以通过在全局组件中添加 v-on="$listeners" 使其继承。

// Globally registered BaseLink.
Vue.component('BaseLinkGlobal', {
  props: {
    href: String
  },
  template: `
    <a
      :href="href"
      class="BaseLinkGlobal"
      v-on="$listeners"
    >
      <slot />
    </a>
  `
})

link 添加后有效:https://codepen.io/jacobgoh101/pen/YvqJxL?editors=0010