
后端路由在浏览器访问匹配路径或前端发起 fetch 请求时均会被触发,但两者目的、行为和适用场景截然不同:前者用于页面跳转与html渲染,后者用于异步数据获取与程序化交互。
在 Web 开发中,一个后端路由(如 app.get(‘/restaurants’, …))本质上是一个服务端请求处理器——它会在任何符合 http 方法(如 GET)且路径完全匹配的请求到达时被调用,无论该请求来自用户手动输入 URL、点击链接跳转,还是由 javaScript 的 fetch() 主动发起。也就是说,路由的“触发”只取决于请求是否抵达服务器并满足匹配条件,而非请求来源。
但关键区别在于请求意图与响应处理方式:
-
✅ 直接导航至 /restaurants(例如在地址栏输入或 点击)
浏览器会发起一个 GET 请求,并期望接收一个可直接渲染的 html 页面。若后端此时返回的是 jsON(如 res.json(ALL_RESTAURANTS)),浏览器通常会将其作为纯文本显示(类似下载文件),界面呈现为一长串难以阅读的 json 字符串,且无法自动解析、绑定到 dom 或响应用户交互。这在绝大多数实际应用中是非预期且不可用的体验。 -
✅ 前端使用 fetch(‘/restaurants’) 发起请求
这是一个程序化、静默、可控的异步请求,不导致页面跳转。开发者可精确控制:
例如,以下前端代码正是典型实践:
const API_ENDPOINT = 'http://localhost:3000'; const loadRestaurants = async () => { try { const response = await fetch(`${API_ENDPOINT}/restaurants`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const restaurants = await response.json(); // ✅ 安全、结构化地渲染数据 const list = document.getElementById('restaurant-list'); list.innerHTML = restaurants.map(r => `${r.name} (ID: ${r.id}) ` ).join(''); } catch (err) { console.error('Failed to load restaurants:', err); document.getElementById('error').textContent = '⚠️ 加载餐厅列表失败,请重试。'; } };
⚠️ 重要注意事项:
立即学习“前端免费学习笔记(深入)”;
- CORS 限制:浏览器对跨域 fetch 请求施加严格同源策略;而直接导航不受 CORS 阻止(但响应内容仍可能因 Content-Type 或 X-Content-Type-Options 被浏览器拒绝渲染)。
- 语义与职责分离:restful 设计中,/restaurants 是数据资源端点,应由前端按需获取并渲染;而 /restaurants 作为 HTML 页面路由(如 Next.js 的 pages/restaurants.js)则属于视图层入口,二者路径可相同,但后端需根据 Accept 头或约定区分响应类型(如返回 HTML 或 JSON),或更推荐采用前后端分离架构(API 与 Web 服务分离部署)。
- 无法替代的操作:仅靠 URL 导航无法执行 POST/PUT/DELETE、携带认证 Token、上传文件或处理复杂错误流——这些必须依赖 fetch 或 axios 等客户端 HTTP 工具。
简言之:路由是“门”,谁敲门都算数;但敲门是为了进门拿东西(fetch),还是为了搬进去住(导航),决定了你准备什么、怎么开门、以及之后做什么。