古法编程: SPA的路由浅思考

0 阅读3分钟

在之前我所有的印象中,往浏览器输入URL,那么就会请求服务器获取资源。毕竟URL又叫统一资源定位器:标识了一个互联网资源的“住址”。

但是 SPA(Single Page Application)单页面应用程序,似乎打破了我这种固有的认知,让我陷入了盲区。我看到它的网络资源地址改变了,但是它并没有向服务器请求?

react-router这家伙就干这样的事情:

  1. 修改了应用程序员的位置URL
  2. 确定在指定的位置渲染哪些React组件

解决盲区痛点

问题1:URL变化了为什么没有请求服务器

URL 变化 ≠ 一定会向服务器发起请求

正常情况下:

用户点击链接 → 地址栏 URL 变化 → 浏览器向新 URL 发送请求 → 服务器返回页面 → 页面刷新

上面是常见的一个思维定式,因此让很多人觉得URL变化请求服务器是一个不可分割的原子操作。实际上:改变 URL 是一个动作,向服务器请求是另一个动作

打开浏览器,我们输入reactrouter.com敲回车来到react-router的官网,打开命令行窗口,输入下面的代码

window.history.pushState({},null,"/home")

结果发现浏览器中的URL已经变化了,但是并没有跳转到home页面

image.png

window.history.pushState

pushState 是 HTML5 提供的 History API 中的一个方法,用于在不刷新页面的情况下向浏览器的会话历史栈中添加一个新的历史记录条目。

问题2: URL变化了此时用户手动刷新为什么不404?

经过上面的案例了解到了window.history.pushState的作用。那么好,假如现在用户手动刷新了页面,此时是会向服务器请求资源的。那么为什么刷新之后看起来是正常页面,而不是404?

正常流程应该是这样会报404的:

1.  用户访问 `https://example.com/`
2.  服务器返回 `index.html`
3.  前端 JS 加载,使用 `pushState` 跳转到 `/user/profile`
4.  地址栏变成 `/user/profile`,页面正常显示 ✅

      **此时用户刷新页面(出问题了):**

5.  用户在 `/user/profile` 页面按 F5 刷新
6.  浏览器向服务器请求 `https://example.com/user/profile`
7.  服务器去找 `/user/profile` 这个路径对应的文件或接口
8.  找不到!返回 404 ❌

        **为什么会 404?**  
        
因为服务器上没有 `/user/profile` 这个真实的文件,它只是前端路由的一个虚拟路径。

但是我们看正常SPA应用程序不会出现404。原因是因为背后的nginx捣的鬼。

nginx配置

try_files 内部回退配置

内部回退的特点:

  • Nginx 内部尝试不同的文件路径
  • 不返回重定向状态码(返回 200)
  • 地址栏 URL 保持不变
  • 用户完全无感知
# Nginx 配置示例:SPA 所有路由都返回 index.html
location / {
  try_files $uri $uri/ /index.html;
}
参数说明
$uri用户请求的原始路径(如 /user/profile
$uri/尝试作为目录查找(如 /user/profile/
/index.html前面都找不到,就返回 index.html

场景:用户刷新 /user/profile

1. 用户访问 https://example.com/user/profile
   ↓
2. Nginx 尝试找文件:$uri = /user/profile
   → 文件不存在
   ↓
3. Nginx 尝试找目录:$uri/ = /user/profile/
   → 目录不存在
   ↓
4. 最后返回 /index.html(不重定向,直接返回这个文件的内容)
   ↓
5. 浏览器收到 index.html
   ↓
6. 前端 JS 重新加载,读取当前 URL 是 /user/profile
   ↓
7. 前端路由匹配到 /user/profile,渲染对应页面
   ↓
8. 用户看到正确的页面 ✅

小结

以上就是关于react-router在单页面应用程序SPA中对我来说神奇的魔法。总结一下,我缺失的盲区

  1. window.history.pushStateAPI
  2. nginx的try_files 内部回退