在 Vue3中,封裝一個(gè) router-links,支持內(nèi)外鏈接都能跳轉(zhuǎn)!
<router-link>標(biāo)簽是用于在Vue應(yīng)用程序的不同頁(yè)面之間跳轉(zhuǎn),但它不是跳轉(zhuǎn)到外部鏈接,相反,我們一般使用<a> 標(biāo)簽。
也許只有我這么認(rèn)為,但很多時(shí)候,我無(wú)法跟上這種差異。其他時(shí)候,鏈接可能是動(dòng)態(tài)的,即來(lái)自數(shù)據(jù)庫(kù)或用戶提供的某個(gè)數(shù)據(jù)源。在這種情況下,你根本不知道鏈接是外部的還是內(nèi)部的,在每個(gè)可能使用鏈接的地方手動(dòng)做一個(gè)v-if是多么痛苦。
如果只用一個(gè)組件來(lái)處理所有內(nèi)部和外部的鏈接,那不是很好嗎?
幸運(yùn)的是,擴(kuò)展<router-link>組件非常簡(jiǎn)單,只需將它包裝到我們自己的定制組件中。Ok,我們需要構(gòu)建一個(gè)AppLink組件來(lái)處理鏈接,無(wú)論是外部的還是內(nèi)部的。
AppLink組件
AppLink組件的 props 要包含 router-link 的所有 props。為什么?因?yàn)檫@樣我們組件的“接口”就可以模仿 Router Link 的接口,無(wú)需再記住另一個(gè)API。我們可以通過(guò)從Vue Router導(dǎo)入 RouterLink 并將其 props 解構(gòu)到我們的組件中,如下所示:
- // AppLink.vue
- <script>
- import {RouterLink} from 'vue-router'
- export default{
- props:{ ...RouterLink.props }
- }
- </script>
在 template 中,創(chuàng)建 router-link 并將 props 傳遞給它,我們還需要傳入slot ,這個(gè)可以在 router-link 插入內(nèi)容。
- // AppLink.vue
- <template>
- <router-link v-bind="$props"><slot /></router-link>
- </template>
到目前為止,我們已經(jīng)處理了所有內(nèi)部鏈接,那外部鏈接呢?如前所述,外部鏈接使用a標(biāo)簽,因此我們將其添加到template中。像 router link 一樣,并將傳入的 to 值賦值給 href。
- // AppLink.vue
- <template>
- <a :href="to"><slot/></a>
- <router-link v-bind="$props"><slot/></router-link>
- </template>
這樣內(nèi)部和外部鏈接都有了對(duì)應(yīng)的處理,需要注意的是,以上內(nèi)容僅適用于 Vue3,因?yàn)樗鄠€(gè)根元素。
現(xiàn)在,我們需要一個(gè)計(jì)算屬性來(lái)告訴AppLink使用哪種鏈接,我們先取名為isExternal。
首先,我們檢查prop的值是否為字符串。這是必需的,因?yàn)閠o屬性可能是一個(gè)對(duì)象,例如有時(shí)傳遞到router-link(即::to="{name:'RouteNameHere'}")。然后,我們將查字符串是否以http字符串開(kāi)頭。如果這兩個(gè)條件都成立,那么就判斷是一個(gè)外部鏈接。
- // AppLink.vue
- <script>
- export default{
- //...
- computed:{
- isExternal(){
- return typeof this.to === 'string' && this.to.startsWith('http')
- }
- }
- }
- </script>
有了 isExternal計(jì)算屬性之后,我們就可以使用 v-if 來(lái)進(jìn)行操作,如下所示:
- // AppLink.vue
- <template>
- <a v-if="isExternal" :href="to"><slot/></a>
- <router-link v-else v-bind="$props"><slot/></router-link>
- </template>
大功告成,我們可以這樣來(lái)使用 AppLink 組件。
- // Anywhere in your app
- <AppLink :to="[external-or-internal-link]">Click Me</AppLink>
更高的靈活性
在新窗口中打開(kāi)
我們可以多添加一些常用的功能。例如,我們希望外部鏈接都在新窗口中打開(kāi),這樣很簡(jiǎn)單就能做到了,只要把 target="_blank" 添加到我們的 a 標(biāo)簽中即可。
- // AppLink.vue
- <template>
- <a ... target="_blank"><slot/></a>
- ...
- </template>
當(dāng)然,有些外部鏈接不需要在新窗口中打開(kāi),我們可以通過(guò)指定 target 來(lái)告訴組件內(nèi)部打開(kāi)鏈接的方式,如下所示:
- <AppLink :to="https://vueschool.io" target="_self">Vue School</AppLink>
鏈接安全
當(dāng)我們使用target="_blank"屬性鏈接到另一個(gè)站點(diǎn)上的頁(yè)面時(shí),最終可能使我們的站點(diǎn)面臨性能和安全性問(wèn)題:
- 鏈接到的頁(yè)面最終可以在與頁(yè)面相同的進(jìn)程上運(yùn)行。根據(jù)所鏈接頁(yè)面的最新情況,這可能會(huì)使您自己的頁(yè)面變慢。
- 另一個(gè)頁(yè)面也可以通過(guò)window.opener屬性訪問(wèn)原始頁(yè)面窗口,從而引起安全隱患。
解決此問(wèn)題的方法是為所有外部鏈接標(biāo)簽添加rel="noopener"屬性,因?yàn)槲覀円呀?jīng)封裝成組件了,所以只需要在組件內(nèi)部的 a 標(biāo)簽添加即可。
- // AppLink.vue
- <template>
- <a ... rel="noopener"><slot/></a>
- ...
- </template>
外部鏈接的獨(dú)特樣式
我見(jiàn)過(guò)一些網(wǎng)站在他們的網(wǎng)站上設(shè)置的外部鏈接樣式與在他們自己的網(wǎng)站上指向站內(nèi)的鏈接有點(diǎn)不同。這可以幫助用戶更好地理解他們要跳轉(zhuǎn)的是外部鏈接。
這個(gè)樣式可以是任何東西,如,在第三方鏈接加個(gè)警告的圖標(biāo),告訴用戶跳轉(zhuǎn)的風(fēng)險(xiǎn)。在我們的組件中實(shí)現(xiàn)這一點(diǎn)非常簡(jiǎn)單,只需在模板中的a標(biāo)簽中添加一個(gè)external-link類,然后使用css對(duì)其進(jìn)行不同的樣式化:
- // 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 思路講完了,當(dāng)然,大家需要新的需求可以自行擴(kuò)展。
~完,我是小智,下期見(jiàn)!
作者:Written by Daniel Kelly
譯者:前端小智 來(lái)源:vueschoolhttps://vueschol.io/articles/vuejs-tutorials/extendig-vue-router-links-in-vue-3/