refactor(theme): refactor link detector, close #754 (#756)

* refactor(theme): refactor link detector

* chore: tweak
This commit is contained in:
pengzhanbo 2025-11-26 01:12:42 +08:00 committed by GitHub
parent 8c1d34cb87
commit f9b8c6adf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 36 additions and 12 deletions

View File

@ -44,6 +44,7 @@ export function linksPlugin(md: Markdown): void {
// convert starting tag of internal link
hasOpenInternalLink = true
token.tag = internalTag
token.attrJoin(':no-icon', 'true')
const matched = hrefLink.match(/^([^#?]*?(?:\/|\.md|\.html))([#?].*)?$/)

View File

@ -29,7 +29,7 @@ function linkTo(e: Event) {
<template>
<Component
:is="tag" class="vp-link" :class="{ link, 'no-icon': noIcon }"
:href="link ? isExternalProtocol ? link : withBase(link) : undefined"
:href="link ? isExternalProtocol ? link : isExternal ? link : withBase(link) : undefined"
:target="target ?? (isExternal ? '_blank' : undefined)"
:rel="rel ?? (isExternal ? 'noreferrer' : undefined)"
@click="linkTo($event)"

View File

@ -1,7 +1,7 @@
import type { ComputedRef, MaybeRefOrGetter } from 'vue'
import { isLinkExternal, isLinkWithProtocol } from '@vuepress/helper/client'
import { computed, toValue } from 'vue'
import { resolveRouteFullPath, useRoute } from 'vuepress/client'
import { resolveRoute, resolveRouteFullPath, useRoute } from 'vuepress/client'
import { useData } from './data.js'
interface UseLinkResult {
@ -24,29 +24,29 @@ export function useLink(
const route = useRoute()
const { page } = useData()
const isExternal = computed(() => {
// 预判断是否可以直接认为是外部链接
// 在此时并不能完全确认是否一定是内部链接
const maybeIsExternal = computed(() => {
const link = toValue(href)
const rawTarget = toValue(target)
if (!link)
return false
if (rawTarget === '_blank' || isLinkExternal(link))
return true
const filename = link.split(/[#?]/)[0]?.split('/').pop() || ''
if (filename === '' || filename.endsWith('.html') || filename.endsWith('.md'))
return false
return filename.includes('.')
return false
})
const link = computed(() => {
// 预处理链接,尝试转为内部的链接
const preProcessLink = computed(() => {
const link = toValue(href)
if (!link)
return undefined
if (isExternal.value)
if (!link || maybeIsExternal.value)
return link
const currentPath = page.value.filePathRelative ? `/${page.value.filePathRelative}` : undefined
const path = resolveRouteFullPath(link, currentPath)
if (path.includes('#')) {
// 将路径 + 锚点 与 当前路由路径进行比较
// 转为锚点链接,避免页面发生刷新
if (path.slice(0, path.indexOf('#')) === route.path) {
return path.slice(path.indexOf('#'))
}
@ -54,6 +54,28 @@ export function useLink(
return path
})
const isExternal = computed(() => {
const link = preProcessLink.value
if (maybeIsExternal.value)
return true
if (!link || link[0] === '#')
return false
// 判断是否为不存在的路由
const routePath = link.split(/[?#]/)[0]
const { notFound } = resolveRoute(routePath)
return notFound
})
const link = computed(() => {
// 外部链接保持原样
if (isExternal.value) {
return toValue(href)
}
return preProcessLink.value
})
const isExternalProtocol = computed(() => {
if (!link.value || link.value[0] === '#')
return false

View File

@ -291,7 +291,8 @@
/**
* External links
* -------------------------------------------------------------------------- */
:is(.vp-external-link-icon, .vp-doc a[href*="://"], .vp-doc a[target=_blank]):not(.no-icon)::after {
:is(.vp-external-link-icon, .vp-doc a[href*="://"],
.vp-doc a[target=_blank])::after {
--icon: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none' /%3E%3Cpath d='M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5H9z' /%3E%3C/svg%3E");
display: inline-block;