
1. 先理清需求Tauri、VuePress、Electron三者到底是什么关系最近有个内部项目要把一份用 VuePress 搭的文档站打包成桌面应用第一反应是上 Electron毕竟生态成熟、案例多。但我实际跑完后发现用 Tauri 不仅能把 VuePress 模板完整塞进桌面壳里安装包体积还小得离谱。很多人会问怎么用 Tauri 创建 VuePress 桌面应用还要搭上 Electron这里其实有个常见误解Tauri 和 Electron 是竞争关系不是组合关系。你真正想要的是像 Electron 那样拥有独立窗口和系统能力但不想背 Electron 那套运行时的效果而 Tauri 恰好能满足这个诉求。这篇文章我会从一个可复用的 VuePress 模板出发讲清楚怎么把它构建成 Tauri 桌面应用同时会穿插和 Electron 的对比。适合谁看三种人一是用 VuePress 写技术文档、想给团队发一个离线版文档工具的人二是已经有点 Electron 基础、想换更轻方案的人三是完全没接触过桌面应用、但熟悉前端构建流程的开发者。跟着走完你不仅能跑通还能知道每个配置项为什么这么写。1.1 这个需求到底在拆解什么先把这句话拆开用 Tauri 创建一个 VuePress 模板构建的桌面应用Electron。标注里的 Electron 有两种理解一种是把 Tauri 误当成 Electron 的别称另一种是希望达到 Electron 的能力但不知道 Tauri 能不能做。实际需求其实很清晰VuePress 负责内容生成桌面壳负责本地化承载最终产物是一个双击就能打开的文档工具。VuePress 这类静态站点生成器非常适合做这种场景因为它输出的是纯 HTML/CSS/JS不需要服务器不依赖数据库天然适合打包到本地。唯一的问题是它默认面向 Web 服务器发布资源路径、路由模式都按 HTTP 方式处理直接塞进桌面壳容易白屏。Electron 的做法是用 Chromium 直接打开本地文件Tauri 的做法是用系统 WebView 加载本地资源两者在资源加载机制上有很多相似点也因此踩的坑也很像。1.2 为什么选 Tauri 而不是 ElectronElectron 最大的痛点就是体积和内存。一个最简单的 Electron 应用安装包轻松 80MB 起步内存占用常常在 200MB 以上因为自带一整套 Chromium。Tauri 的思路完全不同它用 Rust 写后端前端的壳用系统自带的 WebView打包产物极小我个人实测一个 VuePress 文档应用装完只有 6~8MB内存占用也低不少。安全性上 Tauri 也有天然优势。Electron 里如果直接把远程资源塞进 webview程序员得时刻防着 XSS 和任意文件读取而 Tauri 默认配置下前端只能访问白名单里的能力系统 API 要通过插件或命令显式暴露。对文档类应用来说这意味着敏感信息安全边界更清晰不用担心页面里的第三方脚本随意乱调系统功能。当然 Tauri 也有学习成本主要是 Rust 和它的构建链。但如果你只做简单的桌面壳不碰复杂系统能力Rust 部分完全可以不写代码只改配置文件就够了。1.3 VuePress 在 Tauri 里扮演什么角色VuePress 本质上是一个构建工具它把 Markdown 编译成 Vue 单页应用。在 Tauri 方案里VuePress 负责产出 dist 目录Tauri 负责把这个目录当做 WebView 的根目录。这两者的关系就好比内容引擎和外壳容器。也因此你对 VuePress 的配置只需要关注两件事产物路径和资源引用方式。后者尤其关键因为有大量案例是最终 app 图标能显示、窗口能弹出但页面就是白屏排查下来全是base路径写成了绝对路径导致 CSS/JS 加载 404。这个问题在纯静态服务器上不明显因为服务器能以根路径提供服务可一旦换到本地加载协议绝对路径会直接指向文件系统的根命中不了任何资源。所以后文我会专门讲怎么把 VuePress 的路径配置改成相对路径。2. VuePress 侧的准备让静态文档能被桌面壳正常加载2.1 初始化 VuePress 项目不管你是从零开始还是手里已经有一套现成 VuePress第一步都是确认项目能正常npm run build。以 VuePress 2 为例最简初始化是这样的mkdir vuepress-desktop cd vuepress-desktop npm init -y npm install -D vuepressnext mkdir docs echo # Hello VuePress docs/README.md然后在 package.json 里加两个脚本{ scripts: { docs:dev: vuepress dev docs, docs:build: vuepress build docs } }跑一下npm run docs:build如果能在docs/.vuepress/dist目录下看到 index.html 和 assets 目录说明基础构建链路没问题。注意 VuePress 1.x 的产物目录也是一样的所以下面的配置对两个大版本基本通用。2.2 配置 base 路径与构建输出这是 VuePress 桌面化最关键的一步。先在docs/.vuepress/config.js里加上base配置module.exports { base: ./, title: My Desktop Docs }base默认为/表示所有资源路径从网站根目录开始。改成./后生成出来的 HTML 里会使用相对路径这样无论 WebView 加载的是file:///home/user/app/index.html还是 Tauri 自定义协议下的http://tauri.localhost/index.html都能顺着当前文件所在的相对位置找到资源。如果你用的是 VuePress 1.xconfig.js同样支持base。如果你在项目里用了自定义主题或插件某些插件会硬编码绝对路径这种情况下可以检查生成的 HTML 里是否有/assets/xxx.js这样的路径如果有不是改了 base 就能解决得单独看插件配置。2.3 验证静态资源在 file 协议下能正常工作在引入 Tauri 之前先做个快速验证直接用浏览器打开docs/.vuepress/dist/index.html看能不能正常显示样式和交互。如果能说明相对路径配置生效如果白屏按 F12 看控制台一般会报Failed to load resource这时候回到 2.2 检查。还有一类问题是 VuePress 的站内路由。在 Web 服务器上VuePress 用 history 路由做页面跳转但用 file 协议直接打开时刷新子页面会 404。好消息是 Tauri 加载本地静态资源时会走它自己的协议行为更像 HTTP 服务所以大部分 VuePress 站内导航是能用的。万一你遇到点击进去能打开刷新就 404的情况可以暂时把 VuePress 的部署模式定为 hash 模式。VuePress 2 可以在 config 里配置vue-router的相关参数但如果你不想动底层也可以等遇到问题再说因为 Tauri 下遇到的概率不高。3. Tauri 侧的核心配置从模板到可运行桌面应用3.1 初始化 Tauri 并理解目录结构项目内安装 Tauri CLInpm install -D tauri-apps/clilatest npx tauri inittauri init会问你几个问题应用名、窗口标题、Web 资源目录、开发服务器 URL。如果你的项目已经构建好了资源目录填../docs/.vuepress/dist开发服务器 URL 可以先填http://localhost:8080后面再统一调整。初始化完成后会出现src-tauri目录核心是tauri.conf.json。这个文件就是 Tauri 的总配置中心所有关于窗口大小、资源目录、打包行为的设置都在这。和 Electron 的main.jspackage.json相比Tauri 作为一个框架把前后端边界划得特别清楚前端代码依旧在原来的 VuePress 项目里Rust 二进制只负责系统的窗口创建和底层资源访问。3.2 修改 tauri.conf.json 完成资源托管打开src-tauri/tauri.conf.json重点看这几项{ build: { beforeDevCommand: npm run docs:dev, devUrl: http://localhost:8080, beforeBuildCommand: npm run docs:build, distDir: ../docs/.vuepress/dist }, app: { windows: [ { title: My Desktop Docs, width: 1024, height: 768 } ] } }distDir指向 VuePress 构建产物Tauri 在打包时会把这个目录里的所有文件嵌入二进制或作为资源目录一并发布。beforeBuildCommand是关键执行tauri build前Tauri 会先自动跑一次npm run docs:build确保打进安装包的是最新内容省得你每次手动先构建前端再构建桌面端。devUrl的作用是给开发模式用。tauri dev会先启动 VuePress dev server然后让 WebView 加载http://localhost:8080这样你改 Markdown 或者改 Vue 组件都能热更新不用反复重启桌面应用。Electron 里通常也用类似方式但配置分散在多个文件Tauri 把这些收敛到一个配置文件里反而更直观。3.3 用 beforeBuildCommand 把构建流程串起来这里有一个很实用的经验很多人会在 CI 或打包脚本里手动执行先 docs:build再 tauri build其实没必要。Tauri 原生支持beforeBuildCommand你只需要把它指向npm run docs:build它会在每次生成安装包前自动执行。对应地beforeDevCommand指向npm run docs:dev这也解决了 dev server 的端口冲突问题。还要注意 VuePress dev server 默认端口是 8080如果你本地 8080 被占用VuePress 会头铁换端口的那么 Tauri 的devUrl就不能写死 8080。一个稳妥做法是在 package.json 里给 dev script 指定端口docs:dev: vuepress dev docs --port 8090 --no-clear-screen然后把tauri.conf.json里的devUrl改成http://localhost:8090。很多人第一次跑 Tauri 遇到白屏不是资源路径问题而是 dev server 端口跟预期不一致检查这个就对上了。3.4 配置窗口、菜单和系统语言等细节窗口设置建议直接参考 Electron 场景。比如你要做一个内部文档工具窗口大小固定为 1024×768可以禁止缩放如果应用要本地化最好在启动时判断系统语言。Electron 里做这件事一般用app.getLocale()Tauri 这边更简单因为 WebView 是一个浏览器环境直接读navigator.language或者navigator.languages就能拿到系统语言。我在实际项目里验证过Windows 中文系统下navigator.language返回zh-CN和 Electron 的getLocale()结果基本一致而且不需要任何额外权限。菜单栏这块Tauri 默认在 macOS 上会生成一个原生应用菜单在 Windows/Linux 上则没有独立菜单栏这跟 Electron 的默认行为有差异。如果你想做类似 Electron 的文件/编辑/帮助菜单Tauri 官方提供了菜单插件通过tauri-plugin-menu可以创建原生菜单。不过对文档型工具来说我建议初期先别折腾菜单直接把浏览器熟悉的快捷键CtrlC/VCtrlF 等保留好比自定义菜单更实用。4. 完整实操流程与关键代码4.1 从零开始的完整操作步骤假设你已经有一个能npm run docs:build的 VuePress 项目下面是我们实测下来最稳的完整步骤# 1. 在 VuePress 项目根目录安装 Tauri CLI npm install -D tauri-apps/clilatest # 2. 初始化 Tauri npx tauri init # 3. 按提示填写应用名、窗口标题 # 资源目录填../docs/.vuepress/dist # devUrl 填http://localhost:8090 # 4. 修改 VuePress dev 脚本固定端口 npm pkg set scripts.docs:devvuepress dev docs --port 8090 # 5. 修改 VuePress base 为相对路径 # 在 docs/.vuepress/config.js 增加 base: ./ # 6. 修改 tauri.conf.json 的 beforeDevCommand / beforeBuildCommand / distDir # 7. 开发模式运行 npx tauri dev # 8. 打包 npx tauri build注意如果你用的是 VuePress 2docs/.vuepress/config.js里不一定存在这个文件需要手动创建。VuePress 2 对配置文件的写法是defineConfig但为了兼容不同版本上面的模块导出写法在 1.x 和 2.x 下都能工作。4.2 关键配置文件解读这里给一个可以直接抄作业的tauri.conf.json完整示例我注释了每个字段的作用{ productName: vuepress-docs, version: 0.1.0, identifier: com.example.vuepress-docs, build: { beforeDevCommand: npm run docs:dev, devUrl: http://localhost:8090, beforeBuildCommand: npm run docs:build, distDir: ../docs/.vuepress/dist }, app: { windows: [ { title: VuePress Desktop, width: 1024, height: 768, resizable: true, center: true } ], security: { csp: null } }, bundle: { active: true, targets: all, icon: [ icons/32x32.png, icons/128x128.png, icons/128x1282x.png, icons/icon.icns, icons/icon.ico ] } }注意identifier字段Tauri 用它生成应用标识最好用反向域名格式。在 Windows 和 macOS 上这个标识一旦用在了已发布的应用里后面再修改比较麻烦建议一开始就定好。bundle.targets可以指定输出格式Windows 上是msi或nsismacOS 上是app和dmgLinux 是AppImage、deb或rpm填all会按当前平台支持的类型尽量生成。4.3 常见发布问题打包与分发Tauri 打包时默认会把distDir里的文件作为资源嵌入到可执行文件旁边的resources目录所以最终安装包的体积几乎等于前端资源体积加 Rust 二进制体积。如果你的 VuePress 站点里塞了很多大图片、视频或 npm 包资源体积自然会上来建议在 VuePress 里做一次资源压缩泥瓦工式地手动压缩也行。分发这块Electron 受众更广不少人会把安装包丢到各平台跑Tauri 的跨平台构建稍微讲究一些。比如在 Linux 上打 AppImage需要系统里有构建工具和 webkit2gtk 依赖没有的话会报libwebkit2gtk-4.0-dev未找到。我的经验是与其在本机折腾多平台打包不如让 CI 跑Tauri 官方推荐的 GitHub Actions 模板可以直接缓存和构建避免环境差异导致的莫名失败。如果你在国产 Linux 桌面环境做分发Tauri 也能正常构建 deb/rpm 包只是你不能把 Windows 上的构建流程直接搬过去至少要在目标系统里预装 Rust 和 WebKitGTK。这点和 Electron 类似Electron 也需要考虑运行时依赖但 Tauri 的二进制依赖更深建议在正式分发前至少要在一台干净的机器上跑一次安装测试。5. 常见问题与排查技巧实录5.1 白屏与 404资源路径问题白屏绝对是这里最高频的问题。我在调试阶段至少遇到三次无一例外都是资源路径。症状有两种第一种是窗口弹出来全是空白控制台报一堆Not allowed to load local resource或Failed to load module script第二种是页面能显示一部分但图片、CSS 全部失效。第一种通常是 Tauri 的distDir配错了或者构建产物里 index.html 引用了/assets/...这种绝对路径导致 WebView 去根路径找资源。回 VuePress 配置把base: ./加上然后重新npm run docs:build看生成 HTML 里的引用是否变成./assets/...。第二种则是配置了相对路径但 VuePress 插件没完全适配需要手动检查插件输出。为了快速定位白屏原因开发模式建议直接在 Tauri 窗口里右键选择检查前提是启用了 devtools看控制台的错误信息。Tauri 在 debug 模式下会显示 devtoolsrelease 模式下默认不显示这一点和 Electron 类似不用慌。5.2 开发模式如何调试tauri dev的工作机制是先跑beforeDevCommand启动 VuePress dev server再把 WebView 指向 devUrl。所以你在 Tauri 窗口里看到的内容和浏览器里看到的是一样的调试思路也一致。你可以在另一个终端直接 curl devUrl确认页面能不能访问如果返回了 HTML说明 dev server 正常问题在 WebView 侧如果 curl 失败说明端口或 script 有问题。有个小技巧在tauri.conf.json里把app.security.csp先设为null开发阶段能避免一些静态资源被 CSP 拦截的误报。上线前再补严格 CSPTauri 对 CSP 的支持比 Electron 还要直接后者通常要写代理或配置层Tauri 这边直接配置文件搞定。5.3 外部链接跳转与 Electron 对比文档站里经常会放外链比如跳转 GitHub 或官网。在 Electron 里很多新手直接把a写在页面里结果点击后新页面在当前窗口打开体验很怪。Tauri 也有同样问题而且默认安全策略更严格直接打开外部链接会被 WebView 拦截。正确做法是用 Tauri 的 shell/opener 插件。开发模式下先在src-tauri/Cargo.toml里加插件依赖然后在前端调用open命令。如果不方便引入插件还有一个简单替代方案监听前端点击外部链接的事件通过 Tauri 命令让 Rust 调用系统默认浏览器。对文档类应用来说至少要做到内链留在 WebView外链走系统浏览器。5.4 系统语言获取与菜单栏处理热搜词里有人问Electron 获取系统语言Tauri 下其实更轻。直接在 VuePress 的前端代码里拿navigator.language然后根据语言切换 i18n 文案即可。我一开始担心 WebView 的navigator.language和系统语言不一致实测在 Windows 10 中文环境、macOS 英文环境下返回的值都跟系统设置一致所以这是一个零成本方案。菜单栏这块如果你从 Electron 迁移过来会发现 Tauri 默认没有应用菜单。Electron 里Menu.setApplicationMenu是标配Tauri 需要手动引入菜单插件。我建议做一个文档工具时不要原样复刻 Electron 的完整菜单而是保留「文件 - 打印/退出」和「编辑 - 复制粘贴」这类系统常用项其他业务菜单做成前端页面内的按钮这样即减少了系统级代码又不影响用户体验。5.5 问题排查速查表现象可能原因排查与解决窗口打开但白屏distDir 路径不对检查 tauri.conf.json 里的 distDir 是否指向docs/.vuepress/dist白屏且控制台报 404VuePress base 没改成相对路径在 config.js 里设置base: ./重新 builddev 模式白屏dev server 端口变了固定 vuepress dev 的端口并同步 devUrl外链点击没反应WebView 拦截了外链用 opener 插件打开系统浏览器构建失败缺少 webkit 依赖Linux 缺构建依赖安装 libwebkit2gtk-4.0-dev 等系统包安装包体积大前端资源过大压缩图片、按需引入组件再 build我个人在实际操作中的体会是Tauri 和 VuePress 放在一起确实能很优雅地解决把文档站变成桌面应用这件事但前提是你愿意花十分钟把资源路径这套逻辑吃透。只要把这个核心问题想明白了后面的窗口配置、打包分发都是顺水推舟的事。如果后续还想扩展可以再研究 Tauri 的命令系统用 Rust 写一点文件读写、本地缓存等能力整个应用就能从一个文档壳子变成一个真正拥有本地数据能力的工具那是比 Electron 方案更有意思的方向。