前端路由
本教程共 42 篇 · 第 10 篇 · 更新于 2026-08-03
10. 前端路由
本节目标
- 理解桌面端 SPA 路由和网页路由的差别
- 知道为什么 Wails 下推荐用 HashRouter
- 会在 React 里接好 hash 路由
- 看清 BrowserRouter 刷新报错的根因
10-1 桌面端路由的特殊性
做网页时,路由是”浏览器地址栏里的 URL 对应哪个页面”。但桌面应用没有地址栏,用户也基本不会去管那个 URL。可在 Wails 里,前端依然是个 SPA(单页应用),视图切换靠的还是 React Router、Vue Router 这类客户端路由库。路由在桌面端的价值,是”在应用内部切换不同界面”,而不是”对外暴露网址”。
麻烦出在底层。Wails 的前端资源来自 AssetServer——它本质是个按路径找文件的静态服务,从 embed.FS 里取 index.html、JS、CSS。它只认真实存在的文件,不知道你前端路由里的 /settings、/about 是什么。这一点和普通网站不同:网站服务器你能配一条”所有未知路径都回退到 index.html”的兜底规则,而 Wails 的 AssetServer 默认没有这种回退。
NoteSPA(Single Page Application,单页应用)是指整个应用只有一个 HTML 入口,界面切换靠 JavaScript 动态换内容、不改 URL 的真正指向。路由库管的是”当前该显示哪块视图”。
10-2 history 模式为什么在 Wails 里踩坑
React Router 默认是 BrowserRouter,走 HTML5 的 history API(pushState)。访问 /settings 时界面正常,因为路由在客户端拦截了、没真去请求这个路径。但一旦刷新窗口,WebView 会按当前 URL 去 AssetServer 请求 /settings 这个文件——它不存在,于是白屏或报错。
链路是这样的:
- 你在
/settings界面按了刷新(或程序重启后试图恢复这个视图)。 - WebView 向 AssetServer 请求路径
/settings。 - AssetServer 在
embed.FS里找settings这个文件,没有。 - 返回失败,界面挂掉。
这正是很多新手”开发时好好的,刷新就白屏”的根源。开发模式下 Vite 通常会帮你做 SPA 回退,所以问题被藏起来了;一上生产(资源来自 embed、没有回退),立刻暴露。
10-3 hash 模式为什么是桌面端首选
HashRouter 把路由信息放在 URL 的 # 后面,比如 myapp:///#/settings。关键点在于:# 后面的部分不会被发到服务器。无论你刷新 /#/settings 还是 /#/about,WebView 实际向 AssetServer 请求的永远只是根路径 /,AssetServer 老老实实返回 index.html,然后客户端路由再按 hash 把对应视图画出来。
对桌面应用这种”AssetServer 不认客户端路由”的场景,hash 模式天然契合。所以 Wails 官方对 React 的推荐就是 HashRouter。
接法跟普通 React Router 几乎一样,只是换个组件:
import { HashRouter, Routes, Route } from "react-router-dom";
import Home from "./Home";
import Settings from "./Settings";
export default function App() {
return (
<HashRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</HashRouter>
);
}
Tip用 HashRouter 后,URL 里会带个
#。桌面应用没地址栏,用户根本看不见也不在乎,所以这点”不优雅”在桌面端完全可以忽略。换来的是刷新永远不白屏,划算。
10-4 其他框架的对应做法
如果你哪天换框架(本教程主线是 React,这里一笔带过):
- Vue:用
hash模式(Vue Router 的createWebHashHistory),原理和 React 的 HashRouter 一样。 - Angular:用
HashLocationStrategy,也是 hash 思路。 - Svelte:
svelte-spa-router这类本身就是基于 hash 的路由库,天然合适。
共同结论:在 Wails 这类”AssetServer 不回退”的桌面容器里,hash 类路由比 history 类路由省心。
10-5 一定要用 history 怎么办
少数情况你就想要干净的 URL(比如要做可被外部唤起的深链、配合自定义协议)。那就得让 AssetServer 对未知路径回退到 index.html。Wails 的 AssetServer 允许你挂一个自定义的 Handler,在里面判断:请求的文件不存在时,改写请求去取 index.html。
AssetServer: &assetserver.Options{
Assets: assets,
Handler: myFallbackHandler(assets),
},
这个自定义 Handler 属于”动态资产”的范畴,实现细节放到第 11 章讲。绝大多数工具类应用没必要走到这一步,HashRouter 一步到位。
Warning别为了”URL 好看”强行上 BrowserRouter 又不配回退。结果就是刷新白屏、程序重启后视图丢失,排查半天还以为是 Wails 的 bug。桌面端默认上 HashRouter,是踩过无数坑后的共识。
10-6 程序内导航的小建议
用了 HashRouter 之后,在代码里切换视图用 useNavigate 的 navigate("/settings"),和普通 React Router 写法一致,只是底层 URL 变成 #/settings。需要读取当前路由做高亮菜单时,用 useLocation 拿 location.pathname(注意它不包含 # 前面的部分)。
把路由状态存进全局状态(如 Zustand、Redux)时也别和路由库打架——路由是视图状态的真相源,菜单高亮、权限判断都从路由读,别自己再维护一份会漂移的状态。另外,桌面应用没有浏览器那样的前进后退按钮,用户也不会用快捷键在应用内前进后退,所以别依赖 history 栈做关键流程,用显式的导航函数更可控。
10-7 白屏的快速排查顺序
遇到路由相关白屏,按这个顺序查:先看地址栏或控制台里的 URL 是否带 #;没带就说明用了 BrowserRouter,换成 HashRouter;带了 # 仍白屏,再查 window.go 和 window.runtime 是否注入成功,排除是不是绑定问题伪装成路由问题。顺序对了,定位很快。
记住一个根本判断:路由白屏几乎永远是”AssetServer 找不到对应路径”,不是 Go 逻辑错。所以优先怀疑路由模式和刷新行为,而不是去翻业务代码。把这条刻进脑子,能省下大量无谓的调试时间。
常见误区
开发时路由正常就以为生产也正常。Vite dev server 默认做了 SPA 回退,把问题盖住了;wails build 之后资源来自 embed、无回退,history 模式立刻翻车。发布前务必 build 实测刷新。
以为桌面应用必须追求干净 URL。用户看不见地址栏,hash 的 # 对他无感。为了干净 URL 去折腾回退 Handler,投入产出比很低,除非有深链需求。
把”路由刷新白屏”当成绑定失败。两者无关。绑定失败是 window.go 里找不到方法;路由白屏是 AssetServer 找不到路径。先看 URL 有没有 # 就能区分。
小结
桌面端 SPA 路由的关键矛盾是:AssetServer 只认真实文件、不回退客户端路由,所以 BrowserRouter(history 模式)刷新 /settings 会去要不存在的文件而白屏。改用 HashRouter,路由信息在 # 后、不发给服务器,刷新永远拿到 index.html,客户端再按 hash 画视图,稳。React 主线直接换 HashRouter 组件即可;Vue/Angular/Svelte 同理用各自的 hash 方案。非要用 history,就得在第 11 章的自定义 Handler 里做回退。下一章讲动态资产和 AssetServer 的 Handler。