从「页面跳转」到「局部更新」:前端路由的进化之路

108 阅读4分钟

一、传统路由:为什么点击链接会 "白屏"?

在早期的网页开发中,"页面跳转" 是通过<a>标签实现的。这种方式看似简单,却藏着影响用户体验的深层问题。

1.1 传统路由的工作流程

当你点击一个<a href="2.html">链接时,浏览器会执行一系列操作:

  • 向服务器发送新页面的请求(如请求2.html);

  • 等待服务器返回完整的 HTML 文件;

  • 丢弃当前页面的所有内容,重新解析并渲染新 HTML。

这个过程中,从 "丢弃旧内容" 到 "渲染新内容" 的间隙,页面会呈现空白 —— 这就是 "白屏" 的由来。

示例代码解析

<!-- 导航 -->
<nav>
  <ol>
    <li><a href="./1.html">Page 1</a></li>
    <li><a href="./2.html">Page 2</a></li>
  </ol>
</nav>

这里的<a>标签本质是 "全页替换":每次点击都会让浏览器重新加载整个页面,导致状态丢失、等待时间长、白屏频繁。

1.2 核心痛点:用户体验的 "绊脚石"

  • 性能浪费:重复加载公共部分(如导航栏、页脚),明明不需要重新渲染,却被强制刷新;
  • 白屏中断:网络较慢时,白屏时间可长达几秒,用户会误以为页面崩溃;
  • 状态丢失:跳转后页面的滚动位置、输入框内容等状态全部重置。

二、前端路由:SPA 如何实现 "URL 变而页面不刷"?

为解决传统路由的问题,"前端路由" 应运而生,其核心载体是单页应用(SPA)

2.1 什么是 SPA?

SPA(Single Page Application)即 "单页应用",指整个应用只有一个 HTML 页面,但能通过前端技术模拟多页面切换效果。

  • 核心逻辑:用 "组件替换" 代替 "全页刷新";
  • 关键优势:URL 变化时,只更新页面中需要变化的部分(如内容区),导航栏、页脚等公共部分保持不变。

2.2 前端路由的 3 个核心目标

  1. URL 必须变:用户需要看到 URL 变化(方便收藏、回退);
  2. 页面不能刷:禁止浏览器发送新请求、重新渲染整个页面;
  3. 内容动态换:根据 URL 自动切换对应的页面组件(如 / Home→Home 组件,/About→About 组件)。

2.3 实现秘诀:URL 变化不刷新的 2 种方案

URL 变化却不触发刷新,靠的是前端对浏览器 API 的 "巧思利用":

方案原理示例 URL事件监听
hash 模式利用 URL 中#后的哈希值变化http://xxx.com#/homehashchange
history 模式利用 HTML5 的pushStateAPIhttp://xxx.com/home无原生事件(需手动监听)

三、实战:用原生 JS 手写 hash 模式路由

hash 模式是前端路由的 "元老级" 实现,借助 URL 中的#(哈希)特性:#后的内容变化不会触发页面刷新,且会触发hashchange事件。

3.1 核心步骤

  1. <a href="#home">代替<a href="home.html">,通过哈希值标识页面;
  2. 监听hashchange事件,捕捉 URL 中哈希的变化;
  3. 根据当前哈希值,动态更新页面内容区(替换组件)。

3.2 代码逐行解析

<!-- 导航:用#标识不同页面 -->
<ol>
  <li><a href="#home">Home</a></li>
  <li><a href="#about">About</a></li>
  <li><a href="#contact">Contact</a></li>
</ol>

<!-- 内容区:需要动态更新的部分 -->
<div id="content-container">
  <h2>Hello</h2>
  <p>Welcome to our SPA!</p>
</div>

<script>
// 获取内容区DOM
const content = document.getElementById('content-container');

// 监听hash变化:当#后的内容改变时触发
window.addEventListener('hashchange', () => {
  // 根据当前哈希值切换内容
  switch (window.location.hash) {
    case '#home': 
      // 替换内容区为Home页面
      content.innerHTML = '<h2>Home</h2><p>Welcome to homepage</p>';
      break;
    case '#about': 
      content.innerHTML = '<h2>About</h2><p>We are a company</p>';
      break;
    case '#contact': 
      content.innerHTML = '<h2>Contact</h2><p>Contact us</p>';
      break;
  }
})
</script>

效果:点击导航时,URL 中#后的内容变化(如#home#about),内容区动态更新,页面全程不刷新、不白屏。

四、进阶:react-router-dom 玩转 SPA

在 React 项目中,react-router-dom是处理 SPA 路由的 "利器",封装了 hash/history 模式的复杂逻辑,提供简洁的组件 API。

4.1 核心组件解析

组件名作用替代对象
Router包裹整个路由系统(选择 hash/history 模式)无(路由的 "容器")
Link实现 URL 变化(不刷新页面)<a>标签
Routes路由规则的容器无(类似switch
Route定义 "URL→组件" 的映射规则无(类似case

4.2 代码实战

// 引入路由核心组件
import {
  BrowserRouter as Router, 
  Routes,
  Route,
  Link
} from 'react-router-dom'
// 引入页面组件
import Home from './pages/Home'
import About from './pages/About'

function App() {
  return (
    <Router> {/* 路由容器:所有路由相关组件必须在其内部 */}
      {/* 导航:用Link代替a标签,避免刷新 */}
      <nav>
        <ol>
          <li><Link to="/">Home</Link></li> {/* to="/" 对应首页 */}
          <li><Link to="/about">About</Link></li> {/* to="/about" 对应关于页 */}
        </ol>
      </nav>

      {/* 路由规则:URL匹配时显示对应的组件 */}
      <Routes>
        {/* path="/" 匹配根路径,显示Home组件 */}
        <Route path="/" element={<Home />} />
        {/* path="/about" 匹配/about路径,显示About组件 */}
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  )
}

关键细节

  • Linkto属性会通过 JS 修改 URL(history 模式用pushState,hash 模式用哈希),不会触发默认跳转;
  • Routes会根据当前 URL,只渲染匹配的Route组件(类似 "单选");
  • 公共部分(如<nav>)在Routes外部,不会随页面切换更新。

五、总结:路由进化的本质

从传统路由到前端路由,核心变化是 "页面跳转"→"组件切换"

  • 传统路由:依赖后端返回完整 HTML,全页刷新导致体验差;

  • 前端路由:靠 JS 控制 URL 变化 + 组件动态替换,实现 "无刷新切换",SPA 由此诞生。

无论是原生 JS 手写 hash 路由,还是用react-router-dom快速开发,核心目标始终一致:让用户在 URL 变化时,感受到 "无缝切换" 的流畅体验。