如何在Vue中根据链接是内部链接还是外部链接动态渲染<router-link>或<a>标签?

5
<template>
  <component
    :is="type === 'internal' ? 'router-link' : 'a'"
    :to="type === 'internal' ? link : null"
    :href="type !== 'internal' ? link : null"
  >
    <slot />
  </component>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

@Component
export default class SiteLink extends Vue {
  @Prop({
    validator: (value: string) =>
      ["external", "internal"].includes(value)
  })
  private readonly type!: string;

  @Prop({ type: String })
  private readonly link!: string;
}
</script>

上面是一个Vue组件,它将呈现一个链接。我已经删除了与问题无关的任何内容(例如rel、target、class等)。
理解 - 我对Vue Router的理解是,About和About都将在DOM中呈现为About,其中的区别是版本将给链接提供SPA功能(即不加载新页面,而是动态呈现一个组件)。
预期 - 当type="internal"时,它将呈现版本。当type="external"时,它将呈现版本。
<site-link type="external" link="https://stackoverflow.com">Stack Overflow</site-link>

Will render

<a href="https://stackoverflow.com">Stack Overflow</a>

<site-link type="internal" link="/about">About</site-link>

Will render

<router-link to="/about">About</router-link>

Which is then handle by VueRouter to render

<a href="/about">About</a>

实际 - 当 type="internal" 时,<a> 标签在 DOM 中渲染时没有 href 属性。当 type="external" 时,它会按预期渲染。

<site-link type="external" link="https://stackoverflow.com">Stack Overflow</site-link>

Will render

<a href="https://stackoverflow.com">Stack Overflow</a>

<site-link type="internal" link="/about">About</site-link>

Will render

<router-link to="/about">About</router-link>

Which is then handle by VueRouter to render

<a>About</a> <!-- Notice there is no href -->

有什么想法可以帮我实现我想要的吗?


为什么不能同时使用 router-link - Yom T.
我尝试了那个建议,并始终使用router-link来链接内部和外部链接。对于外部链接,它似乎会在传递为属性的“link”前缀中加上localhost:8080(或根URL是其他什么)。 - jackdomleo7
请查看我下面提出的解决方案,让我知道是否有所帮助? - Yom T.
好的,谢谢。让我试一试。 - jackdomleo7
3个回答

5
更好、更干净的方法是:
  <router-link v-if="type === 'internal' :to="link">
    <slot />
  </router-link>
  <a v-else :ref="link"> <slot /> </a>

你可以在根元素中使用 v-if,这样就可以解决你的问题。
或者你可能只是错过了路径部分?
  <component
    :is="type === 'internal' ? 'router-link' : 'a'"
    :to="type === 'internal' ? { path: link } : null"
    :href="type !== 'internal' ? link : null"
  >
    <slot />
  </component>

这将是我的第二种方法,但是我的其他属性,例如reltargetclass等等会在它们之间重复。我试图避免重复属性。 - jackdomleo7
已更新答案,请查看。 - Alex Brohshtut
1
尝试了第二个选项,现在似乎没有渲染任何内容。 - jackdomleo7
1
这是一个非常好的替代方案,唯一的缺点是需要在<router-link><a>中重复属性,例如relclasstarget。如果有一种干净的方法可以建议,避免属性重复,我很乐意将其作为接受的答案。 - jackdomleo7
@JDomleo 如果你同时添加“内部”和“外部”锚点的预期输出会更有帮助,例如要获取哪个属性(或者两个属性都包含是否可以),我假设这些被剥离的属性是为外部链接而设计的,但我不太确定。 - Yom T.
@YomS,请查看已更新的问题,其中包含更新后的期望和实际代码示例。 - jackdomleo7

1

......不同之处在于<router-link>版本会赋予链接SPA功能(即不会加载新页面,它会动态渲染组件)。

不加载新页面,我想你的意思是它不会重新加载页面。是的,它不会,因为onclick处理程序实际上被分配给一个函数执行preventDefault(防止页面重定向),同时将新条目推入历史堆栈

如果您查看API参考文档,最显眼的是<router-link>为您执行的操作是根据活动/当前路由在active-class之间切换。
因此,您可以通过v-slot在默认插槽中进行动态<a>锚点渲染;因为此时,href插槽属性将成为已解析的URL,您可以安全地将其绑定到DOM href属性上。

编辑

添加了一个示例(未经测试)。

<router-link
  :to="link"
  v-slot="{ href, route, navigate, isActive, isExactActive }">
  <a
    :class="isActive ? 'your-custom-class' : 'anything'"
    :href="type !== 'internal' ? href : 'javascript:void(0);'"
    @click="customNavigate(navigate.bind($event))">
    <slot></slot>
  </a>
</router-link>

customNavigate处理程序可能是这样的:

{
  methods: {
    customNavigate(navigate) {
      if (this.type === 'internal') {
        navigate();
        return false;
      }
    }
  }
}

你可以根据插槽属性在组件锚标签上添加任何属性,例如以特定方式导航、添加特殊类名,具体取决于你的使用情况。

嗯,我不确定我是否正确理解了这个问题。你能否可能从问题的代码中给出一个例子?我已经尝试了上面的建议,但我发现很困难。不确定是因为我误解了还是其他原因。 - jackdomleo7
@JDomleo 当然,给我一分钟。 - Yom T.
1
谢谢@Yom S。这是我正在寻找的最接近的答案。但是并不完全一样。 - jackdomleo7
@JDomleo既然我们都同意<router-link>解析为<a>,那么有没有什么特定的原因,我们不能直接跳到最终输出(<a>)呢?难道链接/ href不是这里的关键部分吗? - Yom T.

1
官方文档现在包含一个如何实现这个的例子。
他们的方法是创建一个处理外部链接的自定义模板。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接