
本文介绍一种可靠、跨场景的算法,用于计算两个绝对 URL 之间的最短相对路径(如 /about 或 ../some-other-page),自动处理不同路径层级、查询参数、哈希片段及跨源边界,并确保结果可直接用于 HTML 属性。
本文介绍一种可靠、跨场景的算法,用于计算两个绝对 url 之间的最短相对路径(如 `/about` 或 `../some-other-page`),自动处理不同路径层级、查询参数、哈希片段及跨源边界,并确保结果可直接用于 html `` 属性。
在 Web 开发中,将绝对 URL 转换为语义正确、最短可用的相对 URL 是一个常见但易出错的需求——尤其在静态站点生成、路由重写或富文档导航链接生成等场景中。浏览器原生不提供 URL.relativeTo() 方法,因此需手动实现逻辑严谨的路径归一化与公共前缀裁剪。
核心思路是:先校验协议、主机、端口是否一致(即同源);若不同源,则必须返回完整绝对 URL;若同源,则逐段比对路径数组,计算向上回退(../)次数与剩余向下路径,最后拼接查询参数(search)和哈希(hash)。
以下是生产就绪的 TypeScript/JavaScript 实现(已适配现代环境,无依赖):
function getRelativeURL(from: URL, to: URL): string {
// 跨源:无法用相对路径安全跳转,返回完整绝对 URL
if (from.origin !== to.origin) {
return to.href;
}
// 提取标准化路径段(去除空字符串,保留根 /)
const fromPath = from.pathname === '/' ? [''] : from.pathname.split('/').filter(Boolean);
const toPath = to.pathname === '/' ? [''] : to.pathname.split('/').filter(Boolean);
// 找到最长公共前缀长度
let commonLength = 0;
const minLength = Math.min(fromPath.length, toPath.length);
for (let i = 0; i < minLength; i++) {
if (fromPath[i] === toPath[i]) commonLength++;
else break;
}
// 计算需要多少个 '../' 回退到公共祖先
const upSteps = fromPath.length - commonLength;
const relativePath = Array(upSteps).fill('..').concat(toPath.slice(commonLength));
// 构建路径:空数组 → '/';否则用 '/' 连接
const path = relativePath.length === 0
? '/'
: relativePath.length === 1 && relativePath[0] === ''
? '/'
: '/' + relativePath.filter(p => p !== '').join('/');
// 合并 search 和 hash(保留原始编码)
return path + to.search + to.hash;
}✅ 使用示例:
const from = new URL("https://example.com/blog/post-1");
const to = new URL("https://example.com/about?ref=nav#contact");
console.log(getRelativeURL(from, to));
// 输出: "../../about?ref=nav#contact"
const to2 = new URL("https://example.com/");
console.log(getRelativeURL(from, to2));
// 输出: "../.."
const to3 = new URL("https://other.com/home");
console.log(getRelativeURL(from, to3));
// 输出: "https://other.com/home"(跨源,返回绝对 URL)⚠️ 注意事项:
- 该函数严格遵循 URL 规范:
pathname以/开头,末尾不自动补/;search和hash原样保留(含?和#); - 不处理
file://或data:等非 HTTP 协议(可根据需要扩展); - 若
from或to的pathname为空(如https://ex.com),内部会标准化为[''],确保根路径逻辑正确; - 对于单页应用(SPA),若需支持
#路由跳转,应额外判断to.hash是否独立变化(此时可返回to.hash即可)。
总结:此方案兼顾健壮性与最小化原则——生成的相对 URL 在浏览器中点击时行为完全等价于 to.href,且长度最短、语义清晰,可直接集成至构建工具、MDX 解析器或 CMS 链接处理器中。

















