useExternalRedirect()

useExternalRedirect 是「外链中转页」(/link?url=https://example.com)的逻辑层:读 query 里的目标地址、过安全白名单、倒计时后跳转,同时把这类页面必须做对的 SEO 设置固定住。

中转页要同时做对三件事,而每一件做错当场都看不见:

要做对的事做错的后果谁负责
目标 URL 过 safeExternalUrl 白名单漏了 = javascript: 被赋给 location.href 的 DOM XSS✅ 本 composable 内置
robots: 'noindex'漏了 = 中转页被搜索引擎索引✅ 本 composable 内置(不可覆盖)
页面声明 defineRouteRules({ ssr: false })漏了 = 依赖 query 的页面被 ISR 缓存成第一个访客的目标地址🔴 你自己写(见下)

收编自 9 个 applier 各自的 pages/link.vue(framework#Y-9)。starter 那份历史上改过 12 次,而 applier 副本只跟到第 2 次——补丁不会自动传播,所以把逻辑收进框架、模板留给 applier 自绘。

🔴 页面必须自己声明 ssr: false

这一条 composable 管不到:defineRouteRules 是 Nuxt 的编译期宏,只能写在页面文件里。

<!-- pages/link.vue -->
<script setup lang="ts">
defineRouteRules({ ssr: false }) // 🔴 不能省
definePageMeta({ layout: false })

const { url, seconds } = useExternalRedirect()
</script>

<template>
  <p>即将前往:<a :href="url">{{ url }}</a></p>
  <p><strong>{{ seconds }}</strong> 秒后自动跳转</p>
</template>

漏写会怎样:中转页的内容完全由 ?url= 决定,一旦被 ISR / SWR 缓存,后面所有访客拿到的都是第一个访客的目标地址——这不只是显示错,是把 A 用户的跳转目标发给了 B 用户。

兜底:dev 期该 composable 在服务端执行到时会 console.warn 点名这件事(生产不打印)。但它只在你真的跑到那条路径时才出现,别指望它替你把关。

参数

useExternalRedirect(options?)

名称类型默认值说明
options.delaynumber3000倒计时时长(毫秒,见 TIME-001)。到点执行 location.href = url;传 0 或负数 = 挂载后立即跳转
options.fallbackstring'/'query 里没有合法 http(s) 外链时的回退目标(同样透传 safeExternalUrl)
options.queryKeystring'url'读哪个 query 参数
options.seo{ title?: string; description?: string }内置中文文案覆盖默认 SEO 文案。⚠️ robots: 'noindex' 与 schemaOrg: false 固定,不可覆盖

返回值

名称类型说明
urlstring经白名单校验后的目标地址;非法则等于 fallback
remainingRef<number>剩余毫秒
secondsComputedRef<number>剩余整秒(向上取整),供「N 秒后跳转」文案直接用
go() => void立即跳转(用户点「立即前往」时调)
cancel() => void取消自动跳转(停表;url 仍可点击)

使用示例

<!-- 示例1:最小可用中转页 -->
<script setup lang="ts">
defineRouteRules({ ssr: false })
const { url, seconds } = useExternalRedirect()
</script>

<template>
  <a :href="url">{{ url }}</a>
  <p>{{ seconds }} 秒后自动跳转</p>
</template>
<!-- 示例2:给用户「立即前往 / 取消」两个出口 -->
<script setup lang="ts">
defineRouteRules({ ssr: false })
const { url, seconds, go, cancel } = useExternalRedirect({ delay: 5000 })
</script>

<template>
  <p>{{ seconds }} 秒后前往 {{ url }}</p>
  <SwButton @click="go">立即前往</SwButton>
  <SwButton variant="ghost" @click="cancel">留在本站</SwButton>
</template>
// 示例3:改倒计时 / 回退路径 / query 参数名 / 文案
useExternalRedirect({
  delay: 5000, // 毫秒
  fallback: '/home', // 没有合法外链时回这里
  queryKey: 'target', // 读 ?target= 而不是 ?url=
  seo: { title: 'Redirecting…', description: 'Leaving the site' },
})

注意事项

  • delay 是毫秒,不是秒。倒计时文案用返回的 seconds(已向上取整),别自己拿 delay 去除。
  • robots / schemaOrg 不开放覆盖是有意的:中转页被索引、或带上 JSON-LD,都属于这类页面的典型错误,没有合理的反例场景。
  • fallback 也会过白名单,所以传外站地址同样受约束;想回本站写相对路径即可。
  • 页面模板(样式、品牌、「立即前往」按钮)framework 不提供——各站视觉差异大,只收逻辑不收皮肤。

相关链接