前端路由的奇妙冒险:从稚嫩走向成熟的全过程(中)

204 阅读3分钟

导航

一、传统多页应用的局限

<nav>
  <ul>
    <li><a href="./1.html">Page 1</a></li>
    <li><a href="./2.html">Page 2</a></li>
  </ul>
</nav>

上面的html让我们看到了传统多页应用的实现方式。

这种方式存在明显缺陷:

  1. 每次导航都会触发整页刷新
  2. 需要从服务器重新加载HTML文档
  3. 用户体验不连贯(白屏现象)

二、SPA的雏形:基于hash的路由

SPA(单页应用)是一种Web应用架构

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SPA</title>
</head>
<body>
    <!-- 锚链接 -->
    <a name="top"></a>
    <h1>Navigation</h1>
    <ul>
        <li><a href="#home">Home</a></li>
        <li><a href="#about">About</a></li>
        <li><a href="#contact">Contact</a></li>
    </ul>
    <div id="content-container" class="content">
        Welcome, click on the links above to navigate
    </div>
    <div class="box" style="height: 200vh;">

    </div>
    <a href="#top">回到顶部</a>
    <script>
        const content = document.getElementById('content-container');
        window.addEventListener('hashchange', () => {
            switch(window.location.hash) {
                case '#home': 
                    content.innerHTML = '<h2>Home</h2><p>Welcome to our homepage</p>'
                    break;
                case '#about': 
                    content.innerHTML = '<h2>About</h2><p>Welcome to our aboutpage</p>'
                    break;
                case '#contact': 
                    content.innerHTML = '<h2>Contact</h2><p>Welcome to our contactpage</p>'
                    break;
                default:
                    break;
            }
        })
    </script>
</body>
</html>

上面的html展示了早期SPA的的实现方式:

window.addEventListener('hashchange', () => {
            switch(window.location.hash) {
                case '#home': 
                    content.innerHTML = '<h2>Home</h2><p>Welcome to our homepage</p>'
                    break;
                case '#about': 
                    content.innerHTML = '<h2>About</h2><p>Welcome to our aboutpage</p>'
                    break;
                case '#contact': 
                    content.innerHTML = '<h2>Contact</h2><p>Welcome to our contactpage</p>'
                    break;
                default:
                    break;
            }
        })

这种方案的优点:

  • 无刷新页面切换
  • 通过hashchange事件监听路由变化
  • 局部更新DOM内容

但仍有不足:

  1. URL中带有不美观的#符号
  2. 需要手动管理DOM更新
  3. 缺乏系统化的路由管理

三、现代SPA路由:React Router实践

<a> 标签 到 <Link> 组件的进化

在App.jsx 文件的Router组件里,我们放置了以下的代码。

// App.jsx 中的 <Router>组件中
<nav>
  <ul>
      <li><a href="./">Home</a></li>
      <li><a href="./about">About</a></li>
  </ul>
</nav>

其他的按照前端路由的奇妙冒险:从稚嫩走向成熟的全过程(上)中所说的方法定义路由,并且创建pages

// App.jsx
import { useState } from 'react'
import './App.css'
import { 
  BrowserRouter as Router,
  Routes,
  Route, // 前端路由二选一
} from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'

function App() {
  return (
    <>
      <Router>
        <nav>
          <ul>
              <li><a href="./">Home</a></li>
              <li><a href="./about">About</a></li>
          </ul>
        </nav>
        <Routes>
          <Route path='/' element={<Home/>}/>
          <Route path='/about' element={<About/>}/>
        </Routes>
      </Router>
    </>
  )
}

页面pages就你们自己创建一个页面了,方法上篇文章已经阐述,故不再叙述了。

我们看看效果。

路由3.gif

我们可以看到虽然已经实现了页面的切换,但是页面的刷新过程,会有片刻的白屏。这对用户的体验会有影响,我们该如何解决这个问题呢?

这时,我们就要引入Link组件了

// App.jsx
import { 
  Link
} from 'react-router-dom'

然后把App.jsx中的nav标签的代码替换为

// App.jsx
<nav>
  <ul>
      <li><Link to="/">Home</Link></li>
      <li><Link to="/about">About</Link></li>
  </ul>
</nav>

这时再看看效果

路由4.gif

可以看到没有页面的刷新,恭喜你,成功地实现了无刷路由。

总结

从多页到单页 :

  • 传统:每个页面独立HTML文件
  • SPA:单个HTML + 动态组件切换:
  • 早期:直接操作innerHTML, 使用a标签来发送请求,跳转页面,重新渲染页面,会白屏,用户体验差。
  • 现代:基于组件的声明式开发,使用Link组件,触发事件,只渲染单独组件,不发送请求,实现无刷。用户体验更好。

后面我会聊聊路由是如何充当路由守卫,来进行权限控制的。这就放在下篇文章吧。