插件化大概有这么几个方案,Webview, iframe, shadow DOM。 本质上就是需要一个沙盒 去加载第三方不受信任的资源,为了不影响到本体。
Webview
无论是 Tauri 还是 Electron ,软件窗体结构大概是这样的:
+-------------------------------------+
| Window |
| +-------------------------------+ |
| | WindowApp | |
| | +-------------------------+ | |
| | | Webview | | |
| | +-------------------------+ | |
| +-------------------------------+ |
+-------------------------------------+
Electron 可以在 在页面上套 <webview> 标签来实现加载第三方资源。然而,Tauri 不行。
原因是什么?
因为 Electron 本质上就是一个浏览器,它是 fork 自 chromium 项目,因此 Electron 有 webview 的绝对控制权,想怎么改都行。 但是 Tauri 其实只是一个“内核包装器”,Tauri 在 Windows 上使用 Microsoft Edge WebView2,在 macOS 上使用 WKWebView,在 Linux 上使用 webkitgtk。 这就决定了它无法 webview 进行修改。
所以单纯地像 Electron 那样使用是不可行的。
Electron 还有一个叫 BrowserView 东西,他在窗体结构的大概是这样的:
+-------------------------------------+
| Window |
| +-------------------------------+ |
| | WindowApp | |
| +-------------------------------+ |
| +-------------------------------+ |
| | BrowserView | |
| +-------------------------------+ |
+-------------------------------------+
由于 Tauri 的目标只是做个包装器,因此没有类似于 BrowserView 的东西。
iframe
iframe 其实是浏览器 DOM 标准中的一种内嵌标签,使用 iframe 实现插件容器肯定是可行的。但是其实 iframe 限制很大,比如,如果你要嵌入一些网站,但是很多网站会使用 X-Frame-Options 标头,禁止第三方嵌入 <iframe> 到标签中。
还有一个,iframe 由于是纯前端内嵌,无法转换成独立窗体。其他限制由于我没有 iframe 里深入过,不太了解里面的坑。
shadow DOM
shadow DOM 可以让浏览器将模板、样式表、属性、JavaScript 码等,封装成一个独立的 DOM 元素,从而做到一个相互隔离的环境。从这一点上,它勉强可以当作一种”沙盒“。
但是,shadow DOM 并不是完全的隔离的。有一些方式可以打破封装,比如JavaScript 仍可访问和操作。
最好的选择
从上面来看, webview 好像是一个比较好的选择,但是 tauri 无法直接从一个 webview 里套另一个 webview。
不过好在,Tauri 支持一个 Window 窗口添加多个 webview。
let window = tauri::window::WindowBuilder::new(app, "main")
.inner_size(width, height)
.build()?;
let _webview1 = window.add_child(
tauri::webview::WebviewBuilder::new("main1", WebviewUrl::App(Default::default()))
.auto_resize(),
LogicalPosition::new(0., 0.),
LogicalSize::new(width / 2., height / 2.),
)?;
let _webview2 = window.add_child(
tauri::webview::WebviewBuilder::new(
"main2",
WebviewUrl::External("https://github.com/tauri-apps/tauri".parse().unwrap()),
)
.auto_resize(),
LogicalPosition::new(width / 2., 0.),
LogicalSize::new(width / 2., height / 2.),
)?;
上面的代码会变成下面的样子:
+-------------------------------------+
| Window |
| +-------------------------------+ |
| | WindowApp | |
| | +-------------------------+ | |
| | | <webview> | | <webview> | |
| | +-------------------------+ | |
| +-------------------------------+ |
+-------------------------------------+
同时,我们还得为 Tauri 添加 unstable 这个 features:
tauri = { version = "2.8.4", features = ["unstable"] }