Tauri beta 开发,个人入坑经验
参考我的项目,以避坑
音乐播放器:https://github.com/AClon314/tauri-vuetify-learn
tauri插件(目前可以常驻通知栏以后台保活):https://github.com/AClon314/tauri-plugin-permissionsX/tree/81f78ea
Tauri存在的意义
省流总结:浏览器过于谨慎的更新支持、苹果反对PWA以防对应用商店的生态破坏。Webview没实现的功能,用rust实现。依赖系统webview,减少安装包大小,对linux自带的webview友好(discord的linux版是electron,打包的chrome.dll万年没有更新,导致录屏bug)。
需掌握:
- rust: tauri, serde
- ts + vue
- kotlin: android
硬件条件:
- 最好大于16GB的内存,安卓开虚拟机 + vsCode + Android Studio开logcat + 浏览器开一堆网页
- 一个基础tauri项目的大小,浮动在5~8GB。
cargo clean
之后需要从头编译 - 2块显示屏,切窗口很烦
废话文学
APP vs 网页
浏览器可谓是全世界的互联网标准,W3C说的标准还不算,得看浏览器支不支持,目前仅剩下 谷歌的Chromium、火狐的Gecko、苹果的Webkit的内核,代表着大家的HTML代码,有着3套不同的实现。
微软的2个自研内核都胎死腹中:IE的Trident内核,Edge换内核前的Chakra。可以说明从头写一个浏览器内核是极其艰难的事情。
嵌入式浏览器Servo的内核,从火狐分支出来,现在有Linux欧洲基金会支持,一套在嵌入式设备上HTML的新实现,很期待哦~
但是W3C及其他互联网委员会,对于特性的更新都过于谨慎。试想下,如果打开一个网页,授权后,他就能随意访问本机上所有的文件,你说你的隐私危不危险。
有传闻,苹果其实反对PWA,即一个网页就是一个应用的设计(比如Discord)。这会让以后的APP绕过Apple Store应用商店的审查,安卓同理。所以纯网页的PWA就是个半残品。
目前争气的新特性有WebGPU、文件夹局部访问授权(仅Chromium),其他我还不了解。
我们都知道,APP比网页的权限更多、更自由,毕竟是本机平台代码。在Windows上,除了系统文件,任何一个EXE都可以随便访问、修改任何文件。而在Android上,就必须在运行时弹窗授予权限,给了用户更多知情权与选择权。
但是APP的本机代码是不能跨平台的,安卓、苹果各一套代码,维护时需要花费更多人力物力财力……
跨平台开发
对于个人开发者和公司,这无疑是福音,未来开发的方向——一套前端代码走天下。QQ NT版就已经用C++后端和前端Electron重构了代码。
虽然Electron已经成熟,但是打包还得带个Webview,会让包体积增大大约50~150MB左右,嵌入式就可以说再见了。(虽然公司不在乎用户的存储空间)
而且Tauri用rust编写,内存安全更好(如果你把一部分UI逻辑写到rust里,而不是在前端)。并且目前Tauri更加注重安全性,这体现在写tauri.config.json
中有各种坑、和rust代码里的更多潜在的异常判断……
一些Webview无法做到的功能,就可以用rust来做。比如跨平台设置壁纸的功能。
需预制前端+后端模板
Vite前端+Tauri后端,但Vite具体的框架有很多:Vuetify、Element、Quasar……
一开始用quasar,但后来发现没法搭配Tauri在安卓上运行,发现quasar魔改了配置文件,叫quasar.config.qts
,对vite支持也不好。
最后选用 Vuetify + Tauri,这样就能手动合并vite.config.ts
里的配置了。
然后发现vuetify的网格布局有点坑爹,有些还是用html才实现的布局。
一定要加Discord交流群
Discord搞开发群,好处是比回帖更加活跃,坏处就是没法在谷歌上搜索到
你可以自己试着按官网配,遇到坑的时候看我项目怎么配就行了。
TUN mode 与 ws://localhost:1421 冲突
https://github.com/tauri-apps/tauri/issues/7954
没办法,console内的联网必须开TUN
Tauri/Console: File: http://tauri.localhost/@vite/client - Line 524 - Msg: [vite] server connection lost. polling for restart...
外部存储的访问
- /src-tauri/tauri.conf.json
- assetProtocol ["*/**"]:
*/*
仅代表一层文件夹下的文件,*/**
则代表递归路径 - csp策略:设置不正确会无法加载资源、release白屏
- assetProtocol ["*/**"]:
- /src-tauri/capabilities/mobile.json
- fs:scope文件权限的领域也为
*/**
- fs:scope文件权限的领域也为
安卓授权:https://github.com/tauri-apps/tauri/pull/9311
https://discord.com/channels/616186924390023171/1220085327419674737
解决方案的大意:调用tuari.dialog弹出授权,开发者修改AndroidManifest.xml
的权限,在rust内写一套判断授权成功与否的逻辑。
自己动手,准备PR repo
初始化
pnpm i -g @tauri-apps/cli@next mkdir tauri-plugin-PLUGIN-NAME cd tauri-plugin-PLUGIN-NAME tauri plugin init tauri plugin android init tauri plugin ios init
git pull
如何仅克隆repo的子目录:https://stackoverflow.com/a/73587479/19986873
Windows上,输入sh
(支持Linux shell语法)
function git_sparse_clone_branch() ( local rurl="$1" localdir="$2" branch="$3" && shift 3 git clone "$rurl" --branch "$branch" --no-checkout "$localdir" --depth 1 # limit history cd "$localdir" || return # git sparse-checkout init --cone # fetch only root file git sparse-checkout set "$1" # Loops over remaining args for i; do git sparse-checkout add "$i" # git sparse-checkout set "$i" done git checkout --ignore-other-worktrees "$branch" )
cd D:/repo git_sparse_clone_branch https://github.com/tauri-apps/tauri.git ./dep dev core/tauri/mobile/android/src/main/java/app/tauri
gradle配置:pom依赖
随意更换Gradle版本可能会……
- 构建配置影响:新版本的Gradle可能会对项目的构建配置和逻辑产生影响,因为新版本可能对语法结构、插件API和配置文件进行了调整和优化1。
- 兼容性问题:由于Gradle各版本之间的兼容性可能不佳,升级后相同的构建脚本可能在新版本中报错或产生不同的结果。此外,某些插件可能不支持新版本的Gradle,或者需要更新才能与新版本兼容1。
- 项目构建失败:如果新旧版本之间存在较大差异,可能会导致项目无法构建。因此,在更换版本之前,最好检查项目的构建脚本和插件是否与新版本兼容2。
- Android Studio兼容性:如果你使用的是Android Studio,Gradle版本需要与Android Studio的Gradle插件版本相匹配。如果版本差别过大,可能会导致Android Studio无法正确加载或构建项目2。
- 全局配置:
settings.gradle
- 子目录配置:
build.gradle
或build.gradle.kts
一个项目只有一个settings.gradle
,可以有多个build.gradle
。除非是旧项目,否则新项目一律将所有能统一的配置都写到settings.gradle
(比如仓库)
像package.json就没有历史包袱,嘻嘻
之前删除了%userprofile%
(即C:\Users\用户名)下的.gradle
文件夹,用idea打开Android
自动构建后修复了一些问题
pluginManagement
主要关注插件的管理,而dependencyResolutionManagement
主要关注项目依赖的管理。两者都是为了简化和统一构建配置,但它们作用于构建过程的不同方面。
常用maven仓库
google()
mavenCentral()
maven { url 'https://jitpack.io' }
开发经验上,仓库优先级也是按这样的
2重唱 settings.gradle
- android\
pluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { google() mavenCentral() // 我自己要加的仓库依赖 maven { url 'https://jitpack.io' } } } include ':tauri-android' project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
- examples\tauri-app\src-tauri\gen\android
// 此处需要添加,使其优先使用settings.gradle里的仓库,而不是build.gradle dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url 'https://jitpack.io' } } } include ':app' apply from: 'tauri.settings.gradle'
5重唱 build.gradle.kt
- android
安卓开发环境配置,在这里添加你的项目依赖jar包
-
The supplied phased action failed with an exception.
A problem occurred configuring root project 'android'.
Build file 'D:\tauri-plugin-PLUGIN_NAME\android\build.gradle.kts' line: 1
Plugin [id: 'com.android.library'] was not found in any of the following sources:
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (plugin dependency must include a version number for this source)
这个错误可以忽略。
android/build.gradle.kt
plugins { // if edit kt file: id("com.android.library") version "8.1.1" apply true id("org.jetbrains.kotlin.android") version "1.9.10" apply true // else if tauri dev: // id("com.android.library") // id("org.jetbrains.kotlin.android") } android { namespace = "com.plugin.permissionsx" compileSdk = 32 defaultConfig { minSdk = 21 targetSdk = 33 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } buildTypes { release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } } dependencies { implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.appcompat:appcompat:1.6.0") implementation("com.google.android.material:material:1.7.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") implementation(project(":tauri-android")) implementation("com.github.getActivity:XXPermissions:18.63") // if edit kt file: implementation("androidx.core:core:1.9.0") // 添加 androidx.core 的依赖项 implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2") // 添加 com.fasterxml.jackson 的依赖项 }
android/gradle.properties (我自己项目用的,你不一定要加)
android.useAndroidX=true android.enableJetifier = true
-
Unresolved reference: annotation, plugin kotlin
https://discord.com/channels/616186924390023171/1169271179803119697/1169300947042828409
https://github.com/tauri-apps/tauri/tree/dev/core/tauri/mobile/android/src/main/java/app/tauri
解决unresolved
先把最新kt代码pull下来,然后mklink /d import 下载的仓库
做链接,目录树如下:
android └── src ├── androidTest ├── import 仓库的软链接 │ ├── AndroidManifest.xml │ └── java/app/tauri/** ├── main 你的代码 │ ├── AndroidManifest.xml │ └── java │ ├── Example.kt │ └── PermissionsxPlugin.kt
- examples\tauri-app\src-tauri\gen\android
个人理解:该/build→/buildSrc/build
→/app/build
buildscript { repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:8.0.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") } } allprojects { // 需要注释掉 // repositories { // google() // mavenCentral() // } } tasks.register("clean").configure { delete("build") }
-
examples\tauri-app\src-tauri\gen\android\app
服务于tauri -
examples\tauri-app\src-tauri\gen\android\app\tauri.build.gradle.kts
自动生成 -
examples\tauri-app\src-tauri\gen\android\buildSrc
服务于gradle
pluginsForCoolKids 什么意思
解决vscode对#[cfg(mobile)]条件编译的忽略
https://github.com/rust-lang/rust-analyzer/issues/14395
后来发现没必要,虽然是灰色,忽略的,但仍有语法检查
自定义命令7步走,前端-tauri-kotlin
https://beta.tauri.app/zh-cn/guides/plugins/develop-mobile/#添加移动端命令
1. kotlin @注解类,暴露给tauri
@TauriPlugin class ExamplePlugin(private val activity: Activity): Plugin(activity) { @Command ...
2. src/PLUGIN_NAME.rs,调用kotlin,暴露给command.rs
https://beta.tauri.app/zh-cn/guides/plugins/develop-mobile/#添加移动端命令
3. src/command.rs,#[...]宏,暴露给lib.rs
其实不用写第2步,只是说包装成一个独立的rs文件更好点。
rust内传参可能会用到serde,定义的数据类型都会在models.rs
use tauri::{AppHandle, command, Runtime, State, Window}; use crate::{MyState, Result}; #[command] pub(crate) async fn COMMAND_NAME<R: Runtime>( _app: AppHandle<R>, _window: Window<R>, my_msg: String ) -> Result<String> { Ok(my_msg) }
4. src/lib.rs
https://beta.tauri.app/zh-cn/guides/plugins/#插件配置
原生通知_例子:https://github.com/tauri-apps/plugins-workspace/blob/v2/plugins/notification/src/commands.rs
Builder::new("PLUGIN_NAME") .invoke_handler(tauri::generate_handler![commands::COMMAND_NAME,commands::COMMAND_NAME2])
3~4. build.rs
如果rust内只是个传参操作,可以不用写上面3~4步的样板代码
const COMMANDS: &[&str] = &["ping", "execute","COMMAND_NAME"]; ...
5. capabilities\mobile.json
examples\tauri-app\src-tauri\capabilities\mobile.json
tauri-v2增强了权限安全。加最后一条,否则会报错:Permissions associated with this command...
{ "$schema": "../gen/schemas/mobile-schema.json", "identifier": "mobile-capability", "windows": [ "main" ], "platforms": [ "iOS", "android" ], "permissions": [ "path:default", "path:allow-resolve-directory", "event:default", "event:allow-listen", "app:default", "resources:default", "menu:default", "tray:default", "image:default", "webview:allow-internal-toggle-devtools", "webview:default", "window:default", "PLUGIN_NAME:allow-COMMAND_NAME" ] }
6. .vue/.ts/.js调用&包装invoke,暴露给前端
https://beta.tauri.app/zh-cn/guides/plugins/develop-mobile/#权限许可
import { invoke } from "@tauri-apps/api/core"; async function MY_COMMAND() { greetMsg.value = await invoke("greet", { name: name.value }); console.log(await invoke.length); // 有几种可使用的自定义命令 console.log(await invoke('plugin:PLUGIN_NAME|COMMAND_NAME',{my_msg: "可爱幽灵在线秃头👻"})); }
另一个项目调用本地插件
1. cargo.toml
tauri-plugin-PLUGIN_NAME = { path = "../../tauri-plugin-PLUGIN_NAME/" }
2. tauri.config.json
仅当你的插件在build()过程中接受tauri.config.json内plugin{"PLUGIN_NAME":{}}
时填写
插件内build没有接受参数时,不能填写,否则虽然编译通过,但运行时会崩溃
3. capabilites/mobile.json
"PLUGIN_NAME:allow-MY-COMMAND",
4. package.json
"dependencies": { "@tauri-apps/api": "2.0.0-beta.7", "@tauri-apps/plugin-PLUGIN-NAME": "file:../tauri-plugin-PLUGIN-NAME",
5. lib.rs
这个太坑爹了
.plugin(tauri_plugin_PLUGIN_NAME::init())
更改名字
cargo.toml
[package] name = "my-tauri-app" version = "0.0.1"
为了和插件区分开,build apk时会根据src-tauri\gen\android\app\src\main\java\com\tauri\my_tauri_app
来寻找manifest等文件。
假设插件包名:com.tauri.my_plugin
,会跟你的com.tauri.my_tauri_app
的so库合并,最后才得到com.tauri.viewer
tauri.config.json
{ "productName": "myTauriViewer", "version": "0.0.1", "identifier": "com.tauri.viewer",
也就是说,cargo的version描述com.tauri.my_tauri_app
的rust代码是否有更新;tauri.config.json内的version则可以描述myTauriViewer
整个项目的代码是否有更新,包括前端代码、插件依赖更新等。
最好在cargo
,tauri.config
内确定app的名称后,再tauri android init
,因为android init会根据这些来确定目录名和包名,后期想一个个改,就会很麻烦。
src-tauri\gen\android\app\src\main\res\values\strings.xml
<resources> <string name="app_name">Tauri跨平台demo</string> <string name="main_activity_title">tauri查看器视图</string> </resources>
不知道tauri的多窗口,是否会对应安卓中的动态注册的多activity?
我觉得一个tauri项目就一个静态的activity
插件开发工作流
- 纯kotlin/ObjectC 实现最小示例,提取出需要填的参数
- 移植到rust插件目录内,编写对应rust/ts胶水代码
Cannot find module: src-tauri\tauri
Error: Cannot find module 'D:\Documents\Code\rust\tauri-plugin-permissions\examples\tauri-app\src-tauri\tauri' at Module._resolveFilename (node:internal/modules/cjs/loader:1144:15) at Module._load (node:internal/modules/cjs/loader:985:27) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12) at node:internal/main/run_main_module:28:49 { code: 'MODULE_NOT_FOUND', requireStack: [] } Node.js v20.11.1 w: D:\Documents\Code\rust\tauri-plugin-permissions\android\src\main\java\MyForegroundService.kt: (51, 28): 'constructor Builder(Context!)' is deprecated. Deprecated in Java FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':app:rustBuildX86_64Debug'. > A problem occurred starting process 'command 'd:\Program Files\nodejs\node.exe.cmd''
解决方案:换一个运行时,node.js换bun
- src-tauri\gen\android\buildSrc\src\main\java\com\tauri\tauri_app\kotlin\BuildTask.kt
之前:
@TaskAction fun assemble() { val executable = """C:\Program Files\nodejs\node.exe""";
之后:
@TaskAction fun assemble() { val executable = """C:\Users\Administrator\.bun\bin\bun.exe""";
杂乱总结
build
tauri android build -t aarch64 --apk
keyStore Explorer生成公钥私钥
然后用apkTool签名
release 白屏
logcat日志
14:04:27.962 W Unknown dataspace 0 14:04:28.070 W Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without... 14:04:28.077 W Failed to initialize 101010-2 format, error = EGL_SUCCESS 14:04:28.245 I mapper 4.x is not supported 14:04:28.337 E Unable to match the desired swap behavior. 14:04:28.444 I s_glBindAttribLocation: bind attrib 0 name position 14:04:28.442 W type=1400 audit(0.0:10): avc: denied { getattr } for path="/dev/pmsg0" dev="tmpfs" ino=497 scontext=u:r:untrusted_app_32:s0:c191,c256,c512,c768 tcontext=u:object_r:pmsg_device:s0 tclass=chr_file permissive=0 app=com.tauri.tauri_app 14:04:28.459 I s_glBindAttribLocation: bind attrib 1 name color 14:04:28.858 I Loading com.google.android.webview version 113.0.5672.136 (code 567263637) 14:04:28.922 W Unable to open '/data/app/~~SpmrN2HoNMudJ8YvwwfPyQ==/com.google.android.trichromelibrary_567263637-1xg1MAYdC4W0pEivnvV3Ew==/TrichromeLibrary.dm': No such file or directory 14:04:28.922 W Unable to open '/data/app/~~SpmrN2HoNMudJ8YvwwfPyQ==/com.google.android.trichromelibrary_567263637-1xg1MAYdC4W0pEivnvV3Ew==/TrichromeLibrary.dm': No such file or directory 14:04:28.922 W Entry not found 14:04:28.928 D Configuring clns-7 for other apk /data/app/~~SpmrN2HoNMudJ8YvwwfPyQ==/com.google.android.trichromelibrary_567263637-1xg1MAYdC4W0pEivnvV3Ew==/TrichromeLibrary.apk. target_sdk_version=34, uses_libraries=ALL, library_path=/data/app/~~mJ6pVilfODSYiDD3t6CDyQ==/com.google.android.webview-Y3m5HAZvYVnc-qm6JnEGdQ==/lib/x86_64:/data/app/~~mJ6pVilfODSYiDD3t6CDyQ==/com.google.android.webview-Y3m5HAZvYVnc-qm6JnEGdQ==/WebViewGoogle.apk!/lib/x86_64:/data/app/~~SpmrN2HoNMudJ8YvwwfPyQ==/com.google.android.trichromelibrary_567263637-1xg1MAYdC4W0pEivnvV3Ew==/TrichromeLibrary.apk!/lib/x86_64, permitted_path=/data:/mnt/expand 14:04:28.955 D Configuring clns-8 for other apk /data/app/~~mJ6pVilfODSYiDD3t6CDyQ==/com.google.android.webview-Y3m5HAZvYVnc-qm6JnEGdQ==/WebViewGoogle.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~mJ6pVilfODSYiDD3t6CDyQ==/com.google.android.webview-Y3m5HAZvYVnc-qm6JnEGdQ==/lib/x86_64:/data/app/~~mJ6pVilfODSYiDD3t6CDyQ==/com.google.android.webview-Y3m5HAZvYVnc-qm6JnEGdQ==/WebViewGoogle.apk!/lib/x86_64:/data/app/~~SpmrN2HoNMudJ8YvwwfPyQ==/com.google.android.trichromelibrary_567263637-1xg1MAYdC4W0pEivnvV3Ew==/TrichromeLibrary.apk!/lib/x86_64, permitted_path=/data:/mnt/expand 14:04:29.196 I Loaded version=113.0.5672.136 minSdkVersion=29 isBundle=false multiprocess=true packageId=2 14:04:29.246 I Failed reading seed file "/data/user/0/com.tauri.tauri_app/app_webview/variations_seed_new" 14:04:29.247 I Failed reading seed file "/data/user/0/com.tauri.tauri_app/app_webview/variations_seed" 14:04:29.332 I Successfully loaded native library 14:04:29.338 I Flushed 8 samples from 8 histograms. 14:04:29.611 D Compat change id reported: 183155436; UID 10191; state: ENABLED 14:04:29.999 W [WARNING:dns_config_service_android.cc(115)] Failed to read DnsConfig. 14:04:29.999 I [WARNING:dns_config_service_android.cc(115)] Failed to read DnsConfig. 14:04:30.291 D Compat change id reported: 214741472; UID 10191; state: ENABLED 14:04:30.319 D Compat change id reported: 171228096; UID 10191; state: ENABLED 14:04:30.430 D Fill dialog is enabled:false, hints=[password, passwordAuto, creditCardNumber, creditCardSecurityCode, creditCardExpirationDate] 14:04:30.460 D Compat change id reported: 193247900; UID 10191; state: ENABLED 14:04:30.580 W BLUETOOTH_CONNECT permission is missing. 14:04:30.583 W registerBluetoothIntentsIfNeeded: Requires BLUETOOTH permission
GPT:
- EGL配置失败:日志中提到无法选择带有EGL_SWAP_BEHAVIOR_PRESERVED的配置,这可能意味着EGL环境配置不正确或不支持当前硬件。
- 映射器不支持:日志显示mapper 4.x is not supported,这可能表明您的设备不支持所需的映射器版本。
- 权限问题:日志中的avc: denied { getattr }表明应用没有足够的权限来访问某些文件或目录。
- WebView加载问题:日志中提到无法打开TrichromeLibrary.dm文件,这可能是因为WebView组件没有正确安装或配置。
针对这些问题,您可以尝试以下可能的解决方法:
- 检查EGL配置:确保您的设备支持所需的EGL配置,并且所有相关的库都已正确安装。
- 更新映射器:如果可能,尝试更新或更换映射器版本以匹配您的设备。
- 检查权限:确保应用具有访问所需文件和目录的权限。
- 检查WebView组件:确保WebView组件已正确安装,并且所有相关的依赖文件都在正确的位置。
此外,根据网络搜索结果,有些开发者遇到类似问题时,通过使用箭头函数导入组件,合并打包WebView2的插件依赖,或者修改tauri.conf.json的devPath配置来解决了白屏问题。
思路:重新拉官方模板build一个,发现正常
那就是build后dist内的问题了,也就是网页本身的问题
https://blog.csdn.net/xiaoyao_zhy/article/details/125358416
tauri build --debug
,然后连接remote web console
Refused to load the stylesheet 'https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap' because it violates the following Content Security Policy directive: "default-src 'self' ipc: http://ipc.localhost". Note that 'style-src-elem' was not explicitly set, so 'default-src' is used as a fallback. Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self' ipc: http://ipc.localhost". Either the 'unsafe-inline' keyword, a hash ('sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback. Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'self' ipc: http://ipc.localhost". Either the 'unsafe-inline' keyword, a hash ('sha256-UdfdXjfVHznHxwWlHuNuP27rkN3lBKcVnhZogpI5r78='), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback. EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'sha256-0QpXyJUELr4CvIFf/+i3FujUIuyQIkudC5Z3xCFF58c='".
原来是CSP策略阻止了加载:
- 内联css:应该用
link
加载样式,而不是style
内嵌样式 - eval():很危险的权限,后期一定会改掉
- style-src:需要加载谷歌字体
于是你的tauri.config.json应该这样改:
"security": { "assetProtocol": { "enable": true, "scope": ["*/**"] }, "csp": "default-src 'self' ipc: http://ipc.localhost; style-src 'self' 'unsafe-inline' 'https://fonts.googleapis.com' ipc: http://ipc.localhost; script-src 'self' 'unsafe-eval'; img-src 'self' asset: http://asset.localhost" }
代码逻辑写到前端,还是后端
热重载 | 侧重 | |
---|---|---|
前端 | 快,刷新网页即可 | 调用已有command |
后端 | 慢,需要热编译最后3个crates | webview不支持的功能,自己在rust内实现 |
后端tauri | 区别 | 侧重 |
---|---|---|
event | 双向(前端↔后端) | 持续运行、监听,就是addEventListener() |
command | 单向(前端→后端) | 一次性,就是async function() |
异步vs同步
以下情况才不得不写成异步函数(优先写成同步函数):
应用场景 | 前端 | 后端 |
---|---|---|
IPC跨进程通信 | msedgewebview2.exe: await restart() |
tauri-app.exe: 某一段rust代码Ok(()) |
等待服务器返回 | 本机chrome.exe: await exec('sudo systemctl restart nginx') |
服务器nginx: echo Restart Suc! |
线程是本地代码中,另开线程,还是同一个进程内的,资源共享,不用await
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步