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...

外部存储的访问

  1. /src-tauri/tauri.conf.json
    • assetProtocol ["*/**"]:*/*仅代表一层文件夹下的文件,*/**则代表递归路径
    • csp策略:设置不正确会无法加载资源、release白屏
  2. /src-tauri/capabilities/mobile.json
    • 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.gradlebuild.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包
  1. 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
  1. 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

插件开发工作流

  1. 纯kotlin/ObjectC 实现最小示例,提取出需要填的参数
  2. 移植到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

posted @ 2024-04-12 11:13  Nolca  阅读(691)  评论(0编辑  收藏  举报