简体   繁体   中英

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

<base-link> component

I have a Vue component <base-link> , which I use every time I want to have an achor. It's mostly for applying styles specific to links, so that all the links across the whole page look the same without applying global styles.

Make <router-link> use <base-link>

When using <router-link> component to create a link, I cannot apply those styles ( <base-link> styles are scoped) unless <router-link> uses my <base-link> component to create the anchor element.

Fortunately <router-link> provides tag attribute , which seems to do exactly that. Unfortunately I can't get it to work. I have 2 problems:

  1. All my components are locally registered (I use ES6 modules with Webpack and import components locally every time I need them). <router-link> doesn't know what <base-link> component is and can't render it. Is there a way to inject a local component for <router-link> to use?

  2. To solve problem #1, I thought it's enough to declare <base-link> component globally. Unfortunately it still doesn't work. This time <base-link> component gets rendered properly, but is still not functional - doesn't react to click events. It seems to me the problem is that it's href attribute isn't set at all. Is there a way to make <router-link> set it properly? (without setting it manually)

Question

How do I solve problems #1 and #2? I suspect #1 might be not possible, but I hope at least #2 is.

Code example

Here is a pen with code below, which illustrates both problems.

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>
  `
});

You can create a base link component which can double as a normal a tag or <router-link> when you wish.

//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>

Usage

when you want to use it as a normal link just do not provide the router-link prop as below:

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

To use it as a router-link just add the router-link prop along with the to prop:

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

Explanation about the base-link component:

  • We use a component which is provided by vuejs to render a tag or router-link base on the truthiness of the routerLink prop.

  • A class of .base is added if it is a normal link ie a

  • we bind $attrs which allows us to make the component more transparent ie allows us to use attributes like href or to without passing them as props.

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

You can have a look here for more explanation about usage of $attrs

This is for solving problem #2

The global component doesn't inherit the event listener of the router link. You can make it inherit by adding v-on="$listeners" to the global component.

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

The link works after adding it: https://codepen.io/jacobgoh101/pen/YvqJxL?editors=0010

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM