mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
fix(nuxt): don't set default rel
for same-site external links (#25600)
This commit is contained in:
parent
82173ad1a9
commit
b78e1cb206
@ -57,7 +57,13 @@ In this example, we use `<NuxtLink>` component to link to a website.
|
|||||||
|
|
||||||
## `target` and `rel` Attributes
|
## `target` and `rel` Attributes
|
||||||
|
|
||||||
In this example, we use `<NuxtLink>` with `target`, `rel`, and `noRel` props.
|
A `rel` attribute of `noopener noreferrer` is applied by default to absolute links and links that open in new tabs.
|
||||||
|
- `noopener` solves a [security bug](https://mathiasbynens.github.io/rel-noopener/) in older browsers.
|
||||||
|
- `noreferrer` improves privacy for your users by not sending the `Referer` header to the linked site.
|
||||||
|
|
||||||
|
These defaults have no negative impact on SEO and are considered [best practice](https://developer.chrome.com/docs/lighthouse/best-practices/external-anchors-use-rel-noopener).
|
||||||
|
|
||||||
|
When you need to overwrite this behavior you can use the `rel` and `noRel` props.
|
||||||
|
|
||||||
```vue [app.vue]
|
```vue [app.vue]
|
||||||
<template>
|
<template>
|
||||||
|
@ -20,7 +20,6 @@ import { nuxtLinkDefaults } from '#build/nuxt.config.mjs'
|
|||||||
|
|
||||||
const firstNonUndefined = <T> (...args: (T | undefined)[]) => args.find(arg => arg !== undefined)
|
const firstNonUndefined = <T> (...args: (T | undefined)[]) => args.find(arg => arg !== undefined)
|
||||||
|
|
||||||
const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer'
|
|
||||||
const NuxtLinkDevKeySymbol: InjectionKey<boolean> = Symbol('nuxt-link-dev-key')
|
const NuxtLinkDevKeySymbol: InjectionKey<boolean> = Symbol('nuxt-link-dev-key')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,7 +228,9 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Lazily check whether to.value has a protocol
|
// Lazily check whether to.value has a protocol
|
||||||
const isProtocolURL = computed(() => typeof to.value === 'string' && hasProtocol(to.value, { acceptRelative: true }))
|
const isAbsoluteUrl = computed(() => typeof to.value === 'string' && hasProtocol(to.value, { acceptRelative: true }))
|
||||||
|
|
||||||
|
const hasTarget = computed(() => props.target && props.target !== '_self')
|
||||||
|
|
||||||
// Resolving link type
|
// Resolving link type
|
||||||
const isExternal = computed<boolean>(() => {
|
const isExternal = computed<boolean>(() => {
|
||||||
@ -239,7 +240,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When `target` prop is set, link is external
|
// When `target` prop is set, link is external
|
||||||
if (props.target && props.target !== '_self') {
|
if (hasTarget.value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +249,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return to.value === '' || isProtocolURL.value
|
return to.value === '' || isAbsoluteUrl.value
|
||||||
})
|
})
|
||||||
|
|
||||||
// Prefetching
|
// Prefetching
|
||||||
@ -333,7 +334,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
|
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
|
||||||
const href = typeof to.value === 'object'
|
const href = typeof to.value === 'object'
|
||||||
? router.resolve(to.value)?.href ?? null
|
? router.resolve(to.value)?.href ?? null
|
||||||
: (to.value && !props.external && !isProtocolURL.value)
|
: (to.value && !props.external && !isAbsoluteUrl.value)
|
||||||
? resolveTrailingSlashBehavior(joinURL(config.app.baseURL, to.value), router.resolve) as string
|
? resolveTrailingSlashBehavior(joinURL(config.app.baseURL, to.value), router.resolve) as string
|
||||||
: to.value || null
|
: to.value || null
|
||||||
|
|
||||||
@ -342,10 +343,16 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
|
|
||||||
// Resolves `rel`
|
// Resolves `rel`
|
||||||
checkPropConflicts(props, 'noRel', 'rel')
|
checkPropConflicts(props, 'noRel', 'rel')
|
||||||
const rel = (props.noRel)
|
const rel = firstNonUndefined<string | null>(
|
||||||
? null
|
|
||||||
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
|
// converts `""` to `null` to prevent the attribute from being added as empty (`rel=""`)
|
||||||
: firstNonUndefined<string | null>(props.rel, options.externalRelAttribute, href ? DEFAULT_EXTERNAL_REL_ATTRIBUTE : '') || null
|
props.noRel ? '' : props.rel,
|
||||||
|
options.externalRelAttribute,
|
||||||
|
/*
|
||||||
|
* A fallback rel of `noopener noreferrer` is applied for external links or links that open in a new tab.
|
||||||
|
* This solves a reverse tabnapping security flaw in browsers pre-2021 as well as improving privacy.
|
||||||
|
*/
|
||||||
|
(isAbsoluteUrl.value || hasTarget.value) ? 'noopener noreferrer' : ''
|
||||||
|
) || null
|
||||||
|
|
||||||
const navigate = () => navigateTo(href, { replace: props.replace })
|
const navigate = () => navigateTo(href, { replace: props.replace })
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user