原生 HTML-CSS-JS 前端
本教程共 48 篇 · 第 15 篇 · 更新于 2026-08-09 · 约 6 分钟阅读
本节目标:学完能用一个最朴素的 HTML 文件作为 Tauri 前端跑起来,并理解 Tauri 在开发模式和发布模式下分别是怎么加载这些静态文件的。
Tauri 有个很友好的特性:它对前端框架「一视同仁」。你用 Vue、React 可以,用原生 HTML/CSS/JS 也可以——因为对 Tauri 来说,前端就是一堆静态文件(HTML、CSS、JS,可能还有 WASM),它自己扮演的是一个「静态网站服务器」的角色,把这些文件喂给 WebView 渲染。所以如果你的需求很简单,完全不必引入任何框架,直接用原生三件套最省事。本章就带你认识这种最轻量的前端形态。
为什么选原生前端
不是每个桌面程序都需要复杂的界面。如果你要做的是一个小工具——比如一个本地 Markdown 预览器、一个内部用的配置面板、一个系统托盘配合的小窗口——原生 HTML/CSS/JS 就足够。它的好处很实在:没有框架依赖、没有打包后的庞杂代码、构建产物最小、心智负担最低。
代价也好理解:没有组件化、没有响应式状态管理,界面复杂了之后你得自己手写大量 DOM 操作。所以一句话总结适用场景——「轻量、简单、一个人快速搞定」的界面,用原生;「交互多、页面大、要长期维护」的界面,再考虑框架。
前端项目的目录长什么样
一个用原生前端的最小 Tauri 项目,目录结构大致是这样:
my-app/
├── index.html # 入口页面(必须)
├── styles.css # 你的样式
├── app.js # 你的脚本
└── src-tauri/ # Rust 后端(与前端平级)
├── Cargo.toml
├── tauri.conf.json
└── src/
└── lib.rs
Tauri 加载前端时,会去找 index.html 作为入口,就像浏览器打开一个网站时默认读 index.html 一样。你写的 styles.css、app.js 都通过普通的 <link> 和 <script> 标签被它引用,没有任何特殊之处。
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="styles.css" />
<title>我的小工具</title>
</head>
<body>
<h1>你好,Tauri</h1>
<button id="btn">点我调用 Rust</button>
<p id="out"></p>
<script type="module" src="app.js"></script>
</body>
</html>
Tauri 怎么找到并加载这些文件
关键在 tauri.conf.json 的 build 配置块。它告诉 Tauri 两件事:开发时去哪serve 文件、发布时去哪打包文件。
{
"build": {
"beforeDevCommand": "npx vite --port 5173",
"beforeBuildCommand": "npx vite build",
"devUrl": "http://localhost:5173",
"frontendDist": "../dist"
}
}
逐字段解释:
devUrl:开发模式下,Tauri 直接打开这个网址。它可以是任意静态服务器地址,比如http://localhost:5173。beforeDevCommand:敲tauri dev时,Tauri 会先自动执行这条命令把开发服务器起起来,再打开devUrl。原生前端最简单的做法是用npx vite或npx serve起一个静态服务。frontendDist:发布模式下,Tauri 会把这个目录里的「构建产物」一起打包进安装包。注意路径是相对tauri.conf.json所在目录(src-tauri/)的,所以常见写法是../dist或../out。beforeBuildCommand:敲tauri build时,Tauri 先执行它来生成frontendDist里的内容。
Tip原生前端甚至可以「零构建」:如果你只有一个
index.html加几个静态文件,不需要 Vite,直接用npx serve之类的静态服务器,再把frontendDist直接指到你的源码目录也行。但更规范的做法还是用一个能产出优化后静态文件的工具,保持开发、发布两条路径一致。
开发模式与发布模式加载的区别
同一个 index.html,Tauri 在两种模式下加载方式不同,理解这点能少踩很多坑。
开发模式(tauri dev):Tauri 不碰你的文件,而是打开 devUrl 指向的开发服务器。好处是改了 HTML/JS 能热更新,前端那边怎么调试都行。这时 WebView 其实是在访问一个本地 HTTP 服务。
发布模式(tauri build):Tauri 把 frontendDist 里的文件编译进 Rust 二进制里,通过内部的 asset 协议加载(地址形如 tauri://localhost/index.html)。也就是说,最终用户机器上没有一个正在运行的 Web 服务器,文件是被「嵌」在程序里的。这就是为什么你分发时只需要一个安装包,而不用附带一整个网站目录。
在原生前端里调用 Rust
原生 JS 调用 Rust 命令,和用框架时一模一样,核心还是那句 invoke。因为原生页面通常用 <script type="module"> 直接写 ES 模块,你可以这样:
// app.js
import { invoke } from '@tauri-apps/api/core';
document.querySelector('#btn').addEventListener('click', async () => {
const reply = await invoke('greet', { name: '小明' });
document.querySelector('#out').textContent = reply;
});
这里的 greet 命令,后端(src-tauri/src/lib.rs)的定义和上一章讲过的一模一样。只要你装了 @tauri-apps/api 包,就能这样 import 使用。
如果你连这个 npm 包都不想装,也可以走全局对象的方式(需要在 tauri.conf.json 里开 app.withGlobalTauri: true):
// app.js(无需 import,直接用全局对象)
window.__TAURI__.core.invoke('greet', { name: '小明' }).then((reply) => {
document.querySelector('#out').textContent = reply;
});
Warning原生前端最容易犯的错,是直接在浏览器里双击打开
index.html测试。那样 WebView 不在 Tauri 环境里,invoke会失败,因为tauri://协议和 Rust 后端都不存在。正确做法永远是用tauri dev启动,让 Tauri 真正把页面跑在它的 WebView 中。
小结
原生 HTML/CSS/JS 前端是 Tauri 最轻量的用法:Tauri 当静态服务器,你提供 index.html 入口,开发时由 devUrl 指向的开发服务器喂页面,发布时由 frontendDist 把文件嵌进安装包。调用 Rust 依旧靠 invoke。当你确认界面「小而简单」时,这条路线最干净。
下一章我们把前端升级成更工程化的 Vue 3,你会看到:底层机制完全没变,变的是前端的组织方式和那句 invoke 所在的文件。