<router-link>标签是用于在Vue应用程序的不同页面之间跳转,但它不是跳转到外部链接,相反,我们一般使用<a> 标签。
也许只有我这么认为,但很多时候,我无法跟上这种差异。其他时候,链接可能是动态的,即来自数据库或用户提供的某个数据源。在这种情况下,你根本不知道链接是外部的还是内部的,在每个可能使用链接的地方手动做一个v-if是多么痛苦。
如果只用一个组件来处理所有内部和外部的链接,那不是很好吗?
幸运的是,扩展<router-link>组件非常简单,只需将它包装到我们自己的定制组件中。Ok,我们需要构建一个AppLink组件来处理链接,无论是外部的还是内部的。
AppLink组件
AppLink组件的 props 要包含 router-link 的所有 props。为什么?因为这样我们组件的“接口”就可以模仿 Router Link 的接口,无需再记住另一个API。我们可以通过从Vue Router导入 RouterLink 并将其 props 解构到我们的组件中,如下所示:
- // AppLink.vue
- <script>
- import {RouterLink} from 'vue-router'
- export default{
- props:{ ...RouterLink.props }
- }
- </script>
在 template 中,创建 router-link 并将 props 传递给它,我们还需要传入slot ,这个可以在 router-link 插入内容。
- // AppLink.vue
- <template>
- <router-link v-bind="$props"><slot /></router-link>
- </template>
到目前为止,我们已经处理了所有内部链接,那外部链接呢?如前所述,外部链接使用a标签,因此我们将其添加到template中。像 router link 一样,并将传入的 to 值赋值给 href。
- // AppLink.vue
- <template>
- <a :href="to"><slot/></a>
- <router-link v-bind="$props"><slot/></router-link>
- </template>
这样内部和外部链接都有了对应的处理,需要注意的是,以上内容仅适用于 Vue3,因为它包含多个根元素。
现在,我们需要一个计算属性来告诉AppLink使用哪种链接,我们先取名为isExternal。
首先,我们检查prop的值是否为字符串。这是必需的,因为to属性可能是一个对象,例如有时传递到router-link(即::to="{name:'RouteNameHere'}")。然后,我们将查字符串是否以http字符串开头。如果这两个条件都成立,那么就判断是一个外部链接。
- // AppLink.vue
- <script>
- export default{
- //...
- computed:{
- isExternal(){
- return typeof this.to === 'string' && this.to.startsWith('http')
- }
- }
- }
- </script>
有了 isExternal计算属性之后,我们就可以使用 v-if 来进行操作,如下所示:
- // AppLink.vue
- <template>
- <a v-if="isExternal" :href="to"><slot/></a>
- <router-link v-else v-bind="$props"><slot/></router-link>
- </template>
大功告成,我们可以这样来使用 AppLink 组件。
- // Anywhere in your app
- <AppLink :to="[external-or-internal-link]">Click Me</AppLink>
更高的灵活性
在新窗口中打开
我们可以多添加一些常用的功能。例如,我们希望外部链接都在新窗口中打开,这样很简单就能做到了,只要把 target="_blank" 添加到我们的 a 标签中即可。
- // AppLink.vue
- <template>
- <a ... target="_blank"><slot/></a>
- ...
- </template>
当然,有些外部链接不需要在新窗口中打开,我们可以通过指定 target 来告诉组件内部打开链接的方式,如下所示:
- <AppLink :to="https://vueschool.io" target="_self">Vue School</AppLink>
链接安全
当我们使用target="_blank"属性链接到另一个站点上的页面时,最终可能使我们的站点面临性能和安全性问题:
- 链接到的页面最终可以在与页面相同的进程上运行。根据所链接页面的最新情况,这可能会使您自己的页面变慢。
- 另一个页面也可以通过window.opener属性访问原始页面窗口,从而引起安全隐患。
解决此问题的方法是为所有外部链接标签添加rel="noopener"属性,因为我们已经封装成组件了,所以只需要在组件内部的 a 标签添加即可。
- // AppLink.vue
- <template>
- <a ... rel="noopener"><slot/></a>
- ...
- </template>
外部链接的独特样式
我见过一些网站在他们的网站上设置的外部链接样式与在他们自己的网站上指向站内的链接有点不同。这可以帮助用户更好地理解他们要跳转的是外部链接。
这个样式可以是任何东西,如,在第三方链接加个警告的图标,告诉用户跳转的风险。在我们的组件中实现这一点非常简单,只需在模板中的a标签中添加一个external-link类,然后使用css对其进行不同的样式化:
- // AppLink.vue
- // (must have font awesome font included in project)
- <template>
- <a ... class="external-link">
- <slot/> <i class="fas fa-external-link-alt"></i>
- </a>
- ...
- </template>
- <style scoped>
- .external-link i {
- font-size: 0.8em;
- opacity: 0.7;
- }
- </style>
这里就把 AppLink 思路讲完了,当然,大家需要新的需求可以自行扩展。
~完,我是小智,下期见!
作者:Written by Daniel Kelly
译者:前端小智 来源:vueschoolhttps://vueschol.io/articles/vuejs-tutorials/extendig-vue-router-links-in-vue-3/