文章目录调整

This commit is contained in:
jimmy committed 2025-01-20 14:16:26 +08:00
1 parent 21654b1a92
commit 668119c264
16 files changed
+853 -168

No files matched your search

@@ -1,146 +0,0 @@
---
layout: doc
title: VitePress 笔记使用
date: 2025-01-20
tags:
- 学习
- VitePress
editLink: false
lastUpdated: 2025-01-20T10:09:34
---
## 内部链接
内部链接将转换为单页导航的路由链接。此外,子目录中包含的每个 `index.md` 都会自动转换为 `index.html`,并带有相应的 URL `/`。
[VitePress 链接跳转](./VitePress%20链接跳转.md) <!-- 可以添加 .md -->
```html
[VitePress 链接跳转](./VitePress 链接跳转.md)
```
## YAML front matter
任何包含 [YAML](https://yaml.org/) front matter 块的文件都将是 由 Jekyll 作为特殊文件处理。前言必须是第一件事 ,并且必须采用在三短划线 线。下面是一个基本示例:
``` markdown
---
layout: post
title: Blogging Like a Hacker
---
```
### frontmatter 配置
示例:
``` markdown
---
title: Docs with VitePress
editLink: true
---
```
可以通过 Vue 表达式中的 `$frontmatter` 全局变量访问 frontmatter 数据:
``` Vue
{{ $frontmatter.title }}
```
> 该内容由 Vue 渲染: **{{ $frontmatter.title }}**
## head
- 类型:`HeadConfig[]`
指定要为当前页面注入的额外 head 标签。将附加在站点级配置注入的头部标签之后。
yaml
``` markdown
---
head:
- - meta
- name: description
content: hello
- - meta
- name: keywords
content: super duper SEO
---
```
## layout
- 类型:`doc | home | page`
- 默认值:`doc`
指定页面的布局。
- `doc`——它将默认文档样式应用于 markdown 内容。
- `home`——“主页”的特殊布局。可以添加额外的选项,例如 `hero` 和 `features`,以快速创建漂亮的落地页。
- `page`——表现类似于 `doc`,但它不对内容应用任何样式。当想创建一个完全自定义的页面时很有用。
``` yaml
---
layout: doc
---
```
## navbar
- 类型:`boolean`
- 默认值:`true`
是否显示导航栏。
``` yaml
---
navbar: false
---
```
## sidebar
- 类型:`boolean`
- 默认值:`true`
是否显示 侧边栏
```
---
sidebar: false
---
```
## aside
- 类型:`boolean | 'left'`
- 默认值:`true`
定义侧边栏组件在 `doc` 布局中的位置。
将此值设置为 `false` 可禁用侧边栏容器。
将此值设置为 `true` 会将侧边栏渲染到右侧。
将此值设置为 `left` 会将侧边栏渲染到左侧。
```yaml
---
aside: false
---
```
## lastUpdated
- 类型:`boolean | Date`
- 默认值:`true`
是否在当前页面的页脚中显示最后更新时间的文本。如果指定了日期时间,则会显示该日期时间而不是上次 git 修改的时间戳。
``` yaml
---
lastUpdated: false
---
```
> [!TIP]
> 你必须提交 markdown 文件才能看到最后更新时间。
@@ -1,4 +0,0 @@
# 完成跳转 ,可以点击返回
[返回](./VitePress%20笔记使用.md)
@@ -0,0 +1,181 @@
---
layout: doc
title: tauri 中创建和管理全局 emit 任务
date: 2025-01-21
tags:
- 编程
- Blog
- tauri
- rust
- argus
- 思考
- tokio
head:
- - meta
- name: description
content: 本文详细介绍了如何在 Tauri 中使用全局 Emit 机制来触发错误信息传递,解决后端错误处理问题。通过创建后台异步任务和管理全局事件,实现在没有前端 command 触发的情况下,后端能够主动触发前端事件的解决方案。
- - meta
- name: keywords
content: Tauri emit Tauri错误处理 Tauri全局事件 Tauri后端 emit触发 Tauri command 与 emit Tauri异步任务管理 Tauri后端错误传递 Tauri frontend emit Tauri异步错误处理
editLink: false
lastUpdated: 2025-01-21T10:34:34
---
# Tauri 全局 Emit 任务管理与错误处理机制
> :black_nib: 文章摘要
<!-- DESC SEP -->
在使用 Tauri 开发时,可以通过 `command` 来让前端调用后端,或通过 `emit` 实现后端触发前端事件。`emit` 的设计初衷是为了能够触发多个任务,尤其适用于后端错误信息的传递。在一些场景中,比如后端出错时,利用全局的 `emit` 可以方便地将错误信息传递给前端进行展示或处理。
在 Tauri 中,前端通过 `listen` API 来监听特定事件,而后端则通过 `AppHandle` 中的 `emit` 方法触发这些事件。然而,由于 `AppHandle` 的生命周期问题,它不能作为全局对象存储,这就引发了一个问题:如何在没有前端 `command` 触发的情况下,从后端触发全局的 `emit`。
为了解决这个问题,笔者提出了一种通过创建异步任务管理器的方法。通过使用 Tokio 创建一个后台线程来监听 `mpsc` 通道,后端可以通过该通道发送全局错误消息。当错误发生时,消息通过通道发送到前端,前端通过 `emit` API 处理并展示这些信息。此方案避免了 `AppHandle` 的生命周期问题,并保证了全局事件的触发机制。
<!-- DESC SEP -->
## 前言{#preface}
当在使用 `tauri` 时,可以使用其提供的 `command` 通过前端来调用后端,也可以使用 `emit` 通过后端来调用前端,当后端发生错误时,我们需要一种机制将错误信息传递到前端并进行处理。传统的 `command` 机制依赖于前端调用,而 `emit` 则更适合处理这种场景,因为它支持多个事件的触发。通过在多个地方绑定相同的事件,后端可以通过触发 `emit` 来统一通知前端进行错误处理。
基于此就可以做出很多事情,其中之一就是当后端运行出错时,可以通过全局的 `emit` 将错误信息传递到前端,并通过前端进行展示,或者提示用户错误错误处理,因为后端的报错是不可预知的,所以对于该 `emit` 来说,也应该是全局的,随时可以调用的。
## `emit` 介绍{#emit-introduce}
我们先介绍 `tauri` 中是如何做到 `emit` 的触发和绑定的
#### 前端监听
NPM 软件包提供了用于侦听全局事件和特定于 webview 的事件的 API。`@tauri-apps/api`
```js{9}
import { listen } from '@tauri-apps/api/event';
type DownloadStarted = {
url: string;
downloadId: number;
contentLength: number;
};
listen<DownloadStarted>('download-started', (event) => {
console.log(
`downloading ${event.payload.contentLength} bytes from ${event.payload.url}`
);
});
```
绑定方式通过对一个 '频道' 进行监听,然后来监控是否触发,可以定义接收的数据类型来传递数据
#### 后端触发
```rust{4}
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn download(app: AppHandle, url: String) {
app.emit("download-started", &url).unwrap();
for progress in [1, 15, 50, 80, 100] {
app.emit("download-progress", progress).unwrap();
}
app.emit("download-finished", &url).unwrap();
}
```
后端的触发使用 `AppHandle` 中提供的 `emit` 函数,调用时
1. 通过前端调用后端提供的指令`download` 然后将对应的链接(`url`)传递进来
2. 而 `app: AppHandle` 则由 `tauri` 自动填充,这里的 `app` 就是软件的实例
3. 当拿到 `app` 后,使用其 `emit` 函数进行触发,从而触发前端的监听
整个过程在类似点击下载,后端开始下载,并且前端需要展示下载进度的场景没有什么问题,不过还记得我们的目的吗?我们需要完成的是全局的 `emit` 的触发,并通过此完成错误的提示
这时就会造成一个很严重的问题 :
> 我们的错误是不可预知的,而前端 `emit` 的触发是需要 `AppHandle` 的,> `AppHandle` 作为 Tauri 应用的实例对象,其生命周期和应用的运行状态密切相关,因此不能存储为全局静态对象。在常规情况下,`emit` 触发需要通过 `AppHandle`,但如果依赖前端的 `command` 触发 `emit`,就会陷入循环问题。为了避免这一问题,我们采用了异步线程管理,通过 `tokio` 和 `mpsc` 通道将事件传递给前端。
## 想法{#think}
我们重新整理一下现在需要做什么,现在需要将后端错误通过全局的 `emit` 进行前端,并且想要触发就需要 `AppHandle` ;并且不能将其存储为静态内容。
不过仔细观察官方提供的示例,我们的确无法将 `AppHandle` 进行存储,但是就像官方实例中那样,通过 `Command` 不仅可以拿到 `AppHandle` 实例,还可以通过 `while` 等循环方式让 `emit` 多次触发。
不过我们可以在 `command` 调用时,可以通过 `takio` 创建一个消息等待的线程,并 `takio` 的消息通知渠道保存起来,通过消息通知来传递触发对应的事件
## 创建通用线程处理{#common-thread-processing}
主要的思路是,创建对应的全局的后台任务处理,并通过循环等待
创建一个通用的任务管理器:
``` rust:line-numbers
pub async fn task_h<T, F>(mut rx: mpsc::Receiver<T>, f: F)
where
F: Fn(T),
{
// 无限循环确保 Receiver 一直保持活跃
loop {
match rx.recv().await {
Some(task) => {
f(task);
}
None => {
// 如果通道关闭,可以选择如何处理(这里是等待并继续)
println!("通道关闭,继续等待...");
}
}
}
}
```
`task_h` 函数是一个异步任务管理器,它通过 `mpsc` 通道不断接收任务并执行相应的处理。这个函数的作用是确保事件能够在后台持续触发,而不会受到生命周期的限制。`emit_global_msg` 则是一个全局任务初始化函数,它在首次调用时设置状态并启动一个新的线程来处理错误信息的传递。
使用时,通过将要调用的函数,传递的数据都传递过来,根据我们需求,将会是这样:
```rust{22}
#[tauri::command]
pub fn emit_global_msg(app: AppHandle) {
let mut is_init = GLOBAL_EMIT_IS_INIT.lock().unwrap();
if is_init.clone() {
return;
}
println!("前端 emit 初始化!");
*is_init = true;
let mut emit = GLOBAL_EMIT_APP_HANDLE.lock().unwrap();
let (emit_tx, emit_rx) = mpsc::channel::<String>(100);
*emit = Some(emit_tx);
let f = move |info: String| {
app.clone().emit(global_front_emit::GLOBAL_ERROR_MSG_DISPLAY, info).unwrap();
};
// 在一个新的线程中启动 Tokio 运行时
thread::spawn(move || {
// 创建 Tokio 运行时并运行异步任务
let runtime = tokio::runtime::Runtime::new().unwrap();
runtime.block_on(async move {
task_h(emit_rx, f).await;
});
});
}
```
`GLOBAL_EMIT_APP_HANDLE` 和 `GLOBAL_EMIT_IS_INIT` 分别用于管理全局事件的发送者和初始化状态。通过 `lock` 锁定这些全局状态,确保多次调用时不会重复初始化,从而避免不必要的资源浪费和逻辑错误。
获取到 `AppHandle` 后,我们创建一个闭包,并将具体逻辑传入其中。由于该函数会多次触发,我们需要将 `AppHandle` 移入闭包并进行克隆使用。所有的内容准备好后,创建一个新的 `channel` 将`发送者`保存为全局对象,以供后期使用,而`接收者`交给新线程的任务处理者使用。
最后,通过 `thread::spawn` 启动一个新的线程,并创建一个 `tokio` 运行时来单独运行。
这个函数在进入运行时后,将一直循环和等待任务,`AppHandle` 会被劫持在其中
调用时:
```rust{14}
let emit_option = {
let emit = GLOBAL_EMIT_APP_HANDLE.lock().unwrap();
emit.clone() // 移出作用域以释放锁
};
if let Some(x) = emit_option.as_ref() {
let ms = GlobalErrorMsg {
title: "标题".parse().unwrap(),
msg: "内容".parse().unwrap(),
duration: 5000,
kind: crate::structs::global_error_msg::GlobalErrorMsgTypeEnum::Success,
};
let result = JsonUtil::stringify(&ms).expect("数据序列化失败!");
let qqq = x.send(result).await;
if qqq.is_err() {
println!("发送失败!");
}
}
```
通过拿到保存的`发送者`,并调用其中的 `send` 函数将数据传递到封装的函数中,以完成函数调用。
@@ -0,0 +1,7 @@
---
date: 2025-01-21
title: VitePress 链接使用
---
# 完成跳转 ,可以点击返回
[返回](vitepress-note-use.md)
+621
View File
@@ -0,0 +1,621 @@
---
layout: doc
title: VitePress 笔记使用
date: 2025-01-20
tags:
- 学习
- VitePress
- Blog
- Markdown
editLink: false
lastUpdated: 2025-01-20T10:09:34
head:
- - meta
- name: description
content: 本文介绍了如何使用 vitepress
---
> :black_nib: 文章摘要
<!-- DESC SEP -->
本文介绍了如何使用 vitepress
<!-- DESC SEP -->
## 内部链接
内部链接将转换为单页导航的路由链接。此外,子目录中包含的每个 `index.md` 都会自动转换为 `index.html`,并带有相应的 URL `/`。
[vitepress-link-jump](vitepress-link-jump.md) <!-- 可以添加 .md -->
```html
[VitePress 链接跳转](./VitePress 链接跳转.md)
```
## YAML front matter
任何包含 [YAML](https://yaml.org/) front matter 块的文件都将是 由 Jekyll 作为特殊文件处理。前言必须是第一件事 ,并且必须采用在三短划线 线。下面是一个基本示例:
``` markdown
---
layout: post
title: Blogging Like a Hacker
---
```
### frontmatter 配置
示例:
``` markdown
---
title: Docs with VitePress
editLink: true
---
```
可以通过 Vue 表达式中的 `$frontmatter` 全局变量访问 frontmatter 数据:
``` Vue
{{ $frontmatter.title }}
```
> 该内容由 Vue 渲染: **{{ $frontmatter.title }}**
## head
- 类型:`HeadConfig[]`
指定要为当前页面注入的额外 head 标签。将附加在站点级配置注入的头部标签之后。
yaml
``` markdown
---
head:
- - meta
- name: description
content: hello
- - meta
- name: keywords
content: super duper SEO
---
```
## layout
- 类型:`doc | home | page`
- 默认值:`doc`
指定页面的布局。
- `doc`——它将默认文档样式应用于 markdown 内容。
- `home`——“主页”的特殊布局。可以添加额外的选项,例如 `hero` 和 `features`,以快速创建漂亮的落地页。
- `page`——表现类似于 `doc`,但它不对内容应用任何样式。当想创建一个完全自定义的页面时很有用。
``` yaml
---
layout: doc
---
```
## navbar
- 类型:`boolean`
- 默认值:`true`
是否显示导航栏。
``` yaml
---
navbar: false
---
```
## sidebar
- 类型:`boolean`
- 默认值:`true`
是否显示 侧边栏
```
---
sidebar: false
---
```
## aside
- 类型:`boolean | 'left'`
- 默认值:`true`
定义侧边栏组件在 `doc` 布局中的位置。
将此值设置为 `false` 可禁用侧边栏容器。
将此值设置为 `true` 会将侧边栏渲染到右侧。
将此值设置为 `left` 会将侧边栏渲染到左侧。
```yaml
---
aside: false
---
```
## lastUpdated
- 类型:`boolean | Date`
- 默认值:`true`
是否在当前页面的页脚中显示最后更新时间的文本。如果指定了日期时间,则会显示该日期时间而不是上次 git 修改的时间戳。
``` yaml
---
lastUpdated: false
---
```
> [!TIP]
> 你必须提交 markdown 文件才能看到最后更新时间。
## Emoji
输入:
```html
:tada: :100:
```
输出:
:tada: :100:
## 自定义容器
自定义容器可以通过它们的类型、标题和内容来定义。
**输入**
```md
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
```
**输出**
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
## 自定义标题
可以通过在容器的 "type" 之后附加文本来设置自定义标题。
**输入**
``` md
::: danger STOP
危险区域,请勿继续
:::
::: details 点我查看代码
```js
console.log('Hello, VitePress!')
```
**输出**
::: danger STOP
危险区域,请勿继续
:::
::: details 点我查看代码
```js
console.log('Hello, VitePress!')
```
:::
## GitHub 风格的警报
```markdown
> [!NOTE]
> Useful information that users should know, even when skimming content.
> [!TIP]
> Helpful advice for doing things better or more easily.
> [!IMPORTANT]
> Key information users need to know to achieve their goal.
> [!WARNING]
> Urgent info that needs immediate user attention to avoid problems.
> [!CAUTION]
> Advises about risks or negative outcomes of certain actions.
```
> [!NOTE]
> Useful information that users should know, even when skimming content.
> [!TIP]
> Helpful advice for doing things better or more easily.
> [!IMPORTANT]
> Key information users need to know to achieve their goal.
> [!WARNING]
> Urgent info that needs immediate user attention to avoid problems.
> [!CAUTION]
> Advises about risks or negative outcomes of certain actions.
## 在代码块中实现行高亮
**输入**
```` md
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
````
**输出**
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
除了单行之外,还可以指定多个单行、多行,或两者均指定:
- 多行:例如 `{5-8}`、`{3-10}`、`{10-17}`
- 多个单行:例如 `{4,7,9}`
- 多行与单行:例如 `{4,7-13,16,23-27,40}`
**输入**
```` md
```js{1,4,6-8}
export default { // Highlighted
data () {
return {
msg: `Highlighted!
This line isn't highlighted,
but this and the next 2 are.`,
motd: 'VitePress is awesome',
lorem: 'ipsum'
}
}
}
```
````
**输出**
```js{1,4,6-8}
export default { // Highlighted
data () {
return {
msg: `Highlighted!
This line isn't highlighted,
but this and the next 2 are.`,
motd: 'VitePress is awesome',
lorem: 'ipsum'
}
}
}
```
也可以使用 `// [!code highlight]` 注释实现行高亮。
**输入**
````
```js
export default {
data () {
return {
msg: 'Highlighted!' // [!code highlight]
}
}
}
```
````
**输出**
```js
export default {
data () {
return {
msg: 'Highlighted!' // [!code highlight]
}
}
}
```
## 代码块中聚焦
在某一行上添加 `// [!code focus]` 注释将聚焦它并模糊代码的其他部分。
此外,可以使用 `// [!code focus:<lines>]` 定义要聚焦的行数。
**输入**
````
```js
export default {
data () {
return {
msg: 'Focused!' // [!!code focus]
}
}
}
```
````
**输出**
```js
export default {
data () {
return {
msg: 'Focused!' // [!code focus]
}
}
}
```
## 代码块中的颜色差异 {#colored-diffs-in-code-blocks}
在某一行添加 `// [!code --]` 或 `// [!code ++]` 注释将会为该行创建 diff,同时保留代码块的颜色。
**输入**
````
```js
export default {
data () {
return {
msg: 'Removed' // [!!code --]
msg: 'Added' // [!!code ++]
}
}
}
```
````
**输出**
```js
export default {
data () {
return {
msg: 'Removed' // [!code --]
msg: 'Added' // [!code ++]
}
}
}
```
## 高亮“错误”和“警告” {#errors-and-warnings-in-code-blocks}
在某一行添加 `// [!code warning]` 或 `// [!code error]` 注释将会为该行相应的着色。
**输入**
````
```js
export default {
data () {
return {
msg: 'Error', // [!!code error]
msg: 'Warning' // [!!code warning]
}
}
}
```
````
**输出**
```js
export default {
data() {
return {
msg: 'Error', // [!code error]
msg: 'Warning' // [!code warning]
}
}
}
```
## 行号 {#line-numbers}
可以通过以下配置为每个代码块启用行号:
```js
export default {
markdown: {
lineNumbers: true
}
}
```
查看 [`markdown` 选项](../reference/site-config#markdown) 获取更多信息。
可以在代码块中添加 `:line-numbers` / `:no-line-numbers` 标记来覆盖在配置中的设置。
还可以通过在 `:line-numbers` 之后添加 `=` 来自定义起始行号,例如 `:line-numbers=2` 表示代码块中的行号从 2 开始。
**输入**
````md
```ts {1}
// 默认禁用行号
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers {1}
// 启用行号
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers=2 {1}
// 行号已启用,并从 2 开始
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
````
**输出**
```ts {1}
// 默认禁用行号
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers {1}
// 启用行号
const line2 = 'This is line 2'
const line3 = 'This is line 3'
```
```ts:line-numbers=2 {1}
// 行号已启用,并从 2 开始
const line3 = 'This is line 3'
const line4 = 'This is line 4'
```
## 代码组 {#code-groups}
可以像这样对多个代码块进行分组:
**输入**
````md
::: code-group
```js [config.js]
/**
* @type {import('vitepress').UserConfig}
*/
const config = {
// ...
}
export default config
```
```ts [config.ts]
import type { UserConfig } from 'vitepress'
const config: UserConfig = {
// ...
}
export default config
```
:::
````
**输出**
::: code-group
```js [config.js]
/**
* @type {import('vitepress').UserConfig}
*/
const config = {
// ...
}
export default config
```
```ts [config.ts]
import type { UserConfig } from 'vitepress'
const config: UserConfig = {
// ...
}
export default config
```
:::
## 数学方程 {#math-equations}
现在这是可选的。要启用它,需要安装 `markdown-it-mathjax3`,在配置文件中设置`markdown.math` 为 `true`:
```sh
npm add -D markdown-it-mathjax3
```
```ts [.vitepress/config.ts]
export default {
markdown: {
math: true
}
}
```
**输入**
```md
When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
**Maxwell's equations:**
| equation | description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| $\nabla \cdot \vec{\mathbf{B}} = 0$ | divergence of $\vec{\mathbf{B}}$ is zero |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_ |
```
**输出**
When $a \ne 0$, there are two solutions to $(ax^2 + bx + c = 0)$ and they are
$$ x = {-b \pm \sqrt{b^2-4ac} \over 2a} $$
**Maxwell's equations:**
| equation | description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| $\nabla \cdot \vec{\mathbf{B}} = 0$ | divergence of $\vec{\mathbf{B}}$ is zero |
| $\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} = \vec{\mathbf{0}}$ | curl of $\vec{\mathbf{E}}$ is proportional to the rate of change of $\vec{\mathbf{B}}$ |
| $\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} = 4 \pi \rho$ | _wha?_ |