在 Tauri 应用的分发与运行中,资源打包(将 HTML、JS、CSS、图片等打包进最终的二进制文件)是默认且最稳妥的方式。但实际开发中,经常需要在不重新安装或升级整个应用的前提下,动态替换或加载额外的界面资源、插件、配置文件等——这就是“侧载”(Sideload)的用武之地。
一、默认的资源打包机制
Tauri 使用 tauri-build 工具在编译时将前端构建产物(通常位于 dist/ 或 src-tauri/dist/)打包进 Rust 二进制文件中。启动时,WebView 直接从内存加载这些资源,无需读取本地文件系统。
- 优点:单一执行文件,无额外文件泄露,启动快,不易被篡改。
- 限制:修改任何一个 UI 小细节都需要重新编译、重新签名、重新分发整个安装包。
二、为什么需要侧载
侧载最常见的场景包括:
- 热更新 UI 或静态页面:无需发版即可替换界面、修复紧急文案错误。
- 插件系统:让第三方开发者为你的应用编写 UI 扩展或功能增强模块。
- 按需加载大资源:大型图片、字体、视频等不想打包进基础安装包,而是用户使用时再下载。
- 企业内部定制:同一应用基座,不同部门使用不同的界面主题或功能清单。
三、Tauri 中的侧载实现方案
1. 利用 asset:// 自定义协议加载外部目录
Tauri 允许你注册自定义协议,将磁盘上的物理目录映射为一个虚拟 URL 协议。例如,在 tauri.conf.json 中配置 assetProtocol 范围:
{
"tauri": {
"allowlist": {
"protocol": {
"asset": true,
"assetScope": ["**"]
}
}
}
}
然后在 Rust 侧注册自定义协议:
tauri::Builder::default()
.register_uri_scheme_protocol("myapp-ext", |_app, request| {
// 根据 request.uri() 从本地某个目录读取文件并返回
let path = std::path::PathBuf::from("C:/my-app-plugins")
.join(request.uri().trim_start_matches("myapp-ext://"));
tauri::http::ResponseBuilder::new()
.mimetype(&mime_guess::from_path(&path).first_or_octet_stream().to_string())
.body(std::fs::read(path)?)
})
前端就可以通过 myapp-ext://plugin/index.html 加载外部资源,无需打包进二进制。
2. 动态加载前端页面(iframe / webview 标签)
如果你的应用允许加载任意的第三方页面或插件,可以在 WebView 内通过 <iframe> 或 <webview>(需开启权限)加载本地或远程 URL。但这需要严格的安全检查,防止 XSS 或恶意代码。
3. 本地资源目录的自动扫描与发现
在 Rust 后端扫描特定目录(如 {app_dir}/plugins/*.js),然后通过 window.eval() 或注入脚本的方式将代码注入到 WebView 中运行。注意:直接使用 eval 存在安全风险,更推荐的做法是让插件提供导出函数,通过 IPC 按需调用。
4. 使用 Tauri 的 resources 特性动态加载文件
Tauri 编译时可指定需要一起分发的“资源文件”(如图标、配置文件),它们会被放在安装包同级目录,不嵌进二进制。代码中可通过 tauri::api::path::resource_dir() 获取其路径,然后读取或暴露给前端。
let resource_path = app.path().resource_dir()?;
let config = std::fs::read_to_string(resource_path.join("custom-config.json"))?;
这种方式适合需要让用户或运维人员手动修改的配置文件。
四、安全注意事项
- 校验来源:侧载的脚本或页面必须经过签名校验或哈希比对,防止被篡改。
- 权限隔离:外部加载的内容不应拥有与内部核心页面相同的系统权限,可通过多 WebView 隔离(使用不同的权限配置)实现。
- 禁止滥用
eval:优先用消息通信或预先定义好的函数接口。 - 网络加载:若侧载资源来自远程,务必使用 HTTPS,并考虑检查更新文件的完整性。
五、实战建议
对于大多数工具类应用,推荐优先使用 “资源打包 + 有限的外部配置目录” 的组合:
- 核心 UI 打包在二进制中,保证启动速度和基础体验。
- 将需要用户修改的配置(如 JSON/YAML)放在
resource_dir中,启动时读取。 - 对于真正的插件需求,构建一套基于协议或 IPC 的安全扩展接口,并强制要求插件经过数字签名。
这样既兼顾了应用的完整性,又保留了灵活迭代的空间。