首页 / Wails 入门教程 / 前端路由

Wails 入门教程

前端路由

本教程共 42 篇 · 第 10 篇 · 更新于 2026-08-03

Wails路由React RouterHashRouterSPA

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 默认没有这种回退。

Note

SPA(Single Page Application,单页应用)是指整个应用只有一个 HTML 入口,界面切换靠 JavaScript 动态换内容、不改 URL 的真正指向。路由库管的是”当前该显示哪块视图”。

10-2 history 模式为什么在 Wails 里踩坑

React Router 默认是 BrowserRouter,走 HTML5 的 history API(pushState)。访问 /settings 时界面正常,因为路由在客户端拦截了、没真去请求这个路径。但一旦刷新窗口,WebView 会按当前 URL 去 AssetServer 请求 /settings 这个文件——它不存在,于是白屏或报错。

链路是这样的:

  1. 你在 /settings 界面按了刷新(或程序重启后试图恢复这个视图)。
  2. WebView 向 AssetServer 请求路径 /settings
  3. AssetServer 在 embed.FS 里找 settings 这个文件,没有。
  4. 返回失败,界面挂掉。

这正是很多新手”开发时好好的,刷新就白屏”的根源。开发模式下 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 思路。
  • Sveltesvelte-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 之后,在代码里切换视图用 useNavigatenavigate("/settings"),和普通 React Router 写法一致,只是底层 URL 变成 #/settings。需要读取当前路由做高亮菜单时,用 useLocationlocation.pathname(注意它不包含 # 前面的部分)。

把路由状态存进全局状态(如 Zustand、Redux)时也别和路由库打架——路由是视图状态的真相源,菜单高亮、权限判断都从路由读,别自己再维护一份会漂移的状态。另外,桌面应用没有浏览器那样的前进后退按钮,用户也不会用快捷键在应用内前进后退,所以别依赖 history 栈做关键流程,用显式的导航函数更可控。

10-7 白屏的快速排查顺序

遇到路由相关白屏,按这个顺序查:先看地址栏或控制台里的 URL 是否带 #;没带就说明用了 BrowserRouter,换成 HashRouter;带了 # 仍白屏,再查 window.gowindow.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。