同样是 #,锚点和路由有什么区别

459 阅读4分钟

页面跳转的两种方式:从锚点到路由的完全对比

最近在写官网页面,想到一个问题:URL 里的 # 到底是锚点还是路由?看着浏览器地址栏里的 #/home#/about,又看看传统网页里的 #section1#top,感觉都带 #,但用法完全不一样。

  • 为什么有的 # 后面是页面片段,有的是完整路径?
  • 锚点跳转和路由跳转有啥本质区别?
  • 什么时候用锚点,什么时候用路由?
  • 两者能不能混用?会有什么问题?

锚点:最原始的页内跳转

什么是锚点?

锚点(Anchor)是 HTML 最古老的特性之一,用来在同一个页面内快速跳转到指定位置。它的原理特别简单:

<!-- 定义锚点 -->
<h2 id="section1">第一章</h2>
<div id="top">顶部内容</div>

<!-- 跳转到锚点 -->
<a href="#section1">跳到第一章</a>
<a href="#top">回到顶部</a>

点击链接后,浏览器会自动滚动到对应 id 的元素位置。URL 也会变成 https://example.com/page.html#section1

锚点的工作原理

graph TD
    A["用户点击锚点链接"] --> B["浏览器解析 href='#xxx'"]
    B --> C["查找 id='xxx' 的元素"]
    C --> D{"是否找到元素?"}
    D -->|是| E["滚动到元素位置"]
    D -->|否| F["不做任何操作"]
    E --> G["更新 location.hash"]
    G --> H["触发 hashchange 事件"]

锚点跳转的几个特点:

  1. 不刷新页面 - 纯客户端操作,不发送请求
  2. 自动滚动 - 浏览器原生支持,不需要 JS
  3. 支持后退 - 浏览器历史记录会保存每次跳转
  4. 可以书签 - URL 带锚点可以直接分享和收藏

锚点的现代用法

除了传统的 id 跳转,现代浏览器还支持更灵活的滚动控制:

/* 平滑滚动效果 */
html {
  scroll-behavior: smooth;
}

/* 滚动后的偏移调整 */
h2[id] {
  scroll-margin-top: 60px; /* 避免被固定头部遮挡 */
}

JavaScript 也能更精确地控制:

// 方式1:使用 scrollIntoView
document.getElementById('section1').scrollIntoView({
  behavior: 'smooth',
  block: 'start'
});

// 方式2:监听 hash 变化
window.addEventListener('hashchange', (e) => {
  console.log('从', e.oldURL, '跳到', e.newURL);
  // 自定义跳转逻辑
});

// 方式3:手动设置 hash
location.hash = '#section1';  // 会触发 hashchange 事件

路由:单页应用的核心机制

什么是前端路由?

前端路由(Client-side Routing)是 SPA(单页应用)的核心概念,用来在不刷新页面的情况下切换不同的"页面"内容。说是页面,其实都在同一个 HTML 里,通过 JavaScript 动态切换显示的组件。

// Vue Router 示例
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/user/:id', component: User }
];

// React Router 示例
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/user/:id" element={<User />} />
</Routes>

路由的两种模式

现代前端路由主要有两种实现方式:

1. Hash 路由(Hash Mode)

利用 URL 的 hash 部分(# 后面)来实现:

// URL 格式:https://example.com/#/home
// URL 格式:https://example.com/#/user/123

class HashRouter {
  constructor(routes) {
    this.routes = routes;
    window.addEventListener('hashchange', this.handleRoute.bind(this));
    this.handleRoute(); // 初始化
  }
  
  handleRoute() {
    const hash = location.hash.slice(1) || '/';
    const route = this.routes.find(r => r.path === hash);
    if (route) {
      // 渲染对应组件
      document.getElementById('app').innerHTML = route.component();
    }
  }
  
  push(path) {
    location.hash = path;  // 触发 hashchange
  }
}
2. History 路由(History Mode)

利用 HTML5 的 History API 实现:

// URL 格式:https://example.com/home
// URL 格式:https://example.com/user/123

class HistoryRouter {
  constructor(routes) {
    this.routes = routes;
    window.addEventListener('popstate', this.handleRoute.bind(this));
    this.handleRoute(); // 初始化
  }
  
  handleRoute() {
    const path = location.pathname;
    const route = this.routes.find(r => r.path === path);
    if (route) {
      document.getElementById('app').innerHTML = route.component();
    }
  }
  
  push(path) {
    history.pushState({}, '', path);
    this.handleRoute(); // 手动触发渲染
  }
}

两种路由模式对比

graph LR
    A[Hash路由] --> B[优点]
    B --> B1[无需服务器配置]
    B --> B2[兼容性好]
    B --> B3[部署简单]
    
    A --> C[缺点]
    C --> C1[URL不够美观]
    C --> C2[SEO不友好]
    C --> C3[和锚点冲突]
    
    D[History路由] --> E[优点]
    E --> E1[URL美观]
    E --> E2[更像传统网站]
    E --> E3[SEO友好]
    
    D --> F[缺点]
    F --> F1[需要服务器配置]
    F --> F2[IE9及以下不支持]
    F --> F3[刷新会404]

锚点 vs 路由:详细对比

让我们从多个维度对比两者的区别:

特性锚点(Anchor)Hash 路由History 路由
URL 格式#section1#/page/123/page/123
主要用途页内定位跳转SPA 页面切换SPA 页面切换
触发事件hashchangehashchangepopstate
浏览器行为自动滚动到元素不滚动(除非手动)不滚动
服务器感知不发送给服务器不发送给服务器发送完整路径
SEO 友好度一般
部署配置无需配置无需配置需要服务器配置
浏览器兼容所有浏览器所有浏览器IE10+
刷新页面保持位置保持路由可能 404
实现复杂度简单(原生支持)中等较复杂

总结

研究完锚点和路由,我的理解是:

原理层面

  • 锚点是 HTML 原生特性,用于页内定位
  • Hash 路由劫持了锚点机制,用于 SPA 页面切换
  • History 路由使用 History API,不依赖 hash
  • 两者解决的是不同层次的问题

实用层面

  • 简单的页内跳转用锚点就够了
  • 复杂应用必须用路由系统
  • Hash 路由和锚点有冲突,需要特殊处理
  • History 路由更优雅,但需要服务器配置

参考资源

  1. MDN - HTML Anchors - 锚点的官方文档
  2. MDN - History API - History API 详细说明
  3. Vue Router 文档 - Vue 官方路由实现
  4. React Router 文档 - React 官方路由实现
  5. Can I Use - History API - 浏览器兼容性查询