背景

在前端开发中,我们经常会遇到一些性能瓶颈,比如一些复杂的计算,或者一些需要大量计算的算法,这时候我们可以考虑使用WebAssembly来提高性能。

WebAssembly是一种新的低级字节码格式,可以在现代浏览器中运行,它可以让我们在浏览器中运行其他语言编写的代码,比如C、C++、Rust等。

Rust 因为其安全性可靠性以及高性能,以及跨平台的特性,非常适合用来编写WebAssembly模块。

1
2
3
4
5
6
7
### [Rust 和 WebAssembly 用例](https://developer.mozilla.org/zh-CN/docs/WebAssembly/Guides/Rust_to_Wasm#rust_%E5%92%8C_webassembly_%E7%94%A8%E4%BE%8B#rust_和_webassembly_用例)
Rust 和 WebAssembly 有两大主要用例:

- 构建完整应用——整个 Web 应用都基于 Rust 开发!
- 构建应用的组成部分——在现存的 JavaScript 前端中使用 Rust。

目前,Rust 团队正专注于第二种用例,因此我们也将着重介绍它。对于第一种用例,可以参阅 [`yew`](https://github.com/DenisKolodin/yew) 这类项目。

MDN网站上本身有一个简明的教程,用于介绍如何将Rust编写的程序打包为WebAssembly模块,然后在JavaScript中调用:编译 Rust 为 WebAssembly - WebAssembly | MDN

MDN这篇文章写的非常通俗易懂,基本上是面向于零基础的读者, 但是问题是,这篇文章里面仍然有很多容易踩的坑,所以说,我在这结合我自己的实践、以及所遇到的问题,来写一篇更加详细的教程。

所以说这篇文章的大部分内容将会和 MDN 的文章一致,但是我会对其的一些内容进行删改,以及添加一些我认为有必要的内容。

路径以及阶段性目标

在本教程中,我们将使用 Rust 的 npm 包构建工具 wasm-pack 来构建一个 npm 包。这个包只包含 WebAssembly 和 JavaScript 代码,以便包的用户无需安装 Rust 就能使用。他们甚至不需要知道这里包含 WebAssembly!

之后我们会使用 vite 框架来引入这个npm包,然后在前端项目中调用这个WebAssembly模块。

安装 Rust 环境

让我们看看安装 Rust 环境的所有必要步骤。

安装 Rust

前往 Install Rust 页面并跟随指示安装 Rust。这里会安装一个名为“rustup”的工具,这个工具能让你管理多个不同版本的 Rust。默认情况下,它会安装用于惯常 Rust 开发的 stable 版本 Rust Release。Rustup 会安装 Rust 的编译器 rustc、Rust 的包管理工具 cargo、Rust 的标准库 rust-std 以及一些有用的文档 rust-docs

备注: 需要注意,在安装完成后,你需要把 cargo 的 bin 目录添加到你系统的 PATH。一般来说它会自动添加,但需要你重启终端后才会生效。

wasm-pack

要构建我们的包,我们需要一个额外工具 wasm-pack。它会帮助我们把我们的代码编译成 WebAssembly 并制造出正确的 npm 包。使用下面的命令可以下载并安装它:

1
cargo install wasm-pack

可选:安装 Node.js 并获取 npm 账户

在这个例子中我们将会构建一个 npm 包,因此你需要确保安装 Node.js 和 npm 已经安装。

另外,我们将会把包发布到 npm 上,因此你还需要一个 npm 账号。它们是免费的。发布这个包并不是必须的,但是发布它非常简单,因此在本例中我们默认你会发布这个包。

这一步主要是为了,让你能够在本地测试你的WebAssembly模块,以及发布到npm上。之后其他的项目可以直接通过npm来同步更新你的WebAssembly模块,而无需手动操作。

Get npm! 页面按照说明下载并安装 Node.js 和 npm。在选择版本时,选择一个你喜欢的版本;本例不限定特定版本。

npm signup page 注册 npm 账户,并填写表格。

接下来,在命令行中运行 npm adduser:

1
2
3
4
> npm adduser
Username: yournpmusername
Password:
Email: (this IS public) you@example.com

你需要完善你的用户名,密码和邮箱。如果成功了,你将会看到:

1
Logged in as yournpmusername on https://registry.npmjs.org/.

在这里你可能会遇到两个问题: 1. 你被重定向到了 cnpm 而不是 npm 的网站。cnpm不支持公开账号的注册,它只允许你注册一个私人账号。

遇到这个问题是因为你之前可能遇到过 npm 无法安装某些模块的问题,之后在教程的指引下,使用了npmmirrow 淘宝镜像作为 npm 的源,当更改npm的源的时候,账号验证的源也会被设置为淘宝的镜像源。

解决方式有两个,一个是直接切换npm的源,但是这样,当你下次又有无法安装的而模块的时候,你又需要切换回来,最好的办法是自己手动指定一下登入账号使用的源:

1
npm login --registry http://registry.npmjs.com

  1. 密码和账号输入时无法确认是否输入正确且无法复制粘贴。

使用

1
npm login --username <username> --password <password> --email <email>

可以在命令行就指定这些参数,方便检查有无出错。 但是请注意请不要将这行命令添加到公开的文件里,以保护你的账号安全。

构建我们的 WebAssembly npm 包

万事俱备,来创建一个新的 Rust 包吧。打开你用来存放你私人项目的目录,做这些事:

1
2
$ cargo new --lib hello-wasm
Created library `hello-wasm` project

这里会在名为 hello-wasm 的子目录里创建一个新的库,里面有下一步之前你所需要的一切:

1
2
3
+-- Cargo.toml
+-- src
+-- lib.rs

首先,我们有一个 Cargo.toml 文件,这是我们配置构建的方式。如果你用过 Bundler 的 Gemfile 或者 npm 的 package.json,你应该会感到很熟悉。Cargo 的用法和它们类似。

接下来,Cargo 在 src/lib.rs 生成了一些 Rust 代码:

1
2
3
4
5
6
7
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

我们完全不需要使用这些测试代码,所以继续吧,我们删掉它。

来写点 Rust 代码吧!

让我们在 src/lib.rs 写一些代码替换掉原来的:

1
2
3
4
5
6
7
8
9
10
11
12
13
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
alert(&format!("Hello, {}!", name));
}

上面是MDN给的代码,它在后面给出了每个部分的解释,你可以点击这里跳转到原文进行查看:来写点 Rust 代码吧!

不过我认为将注释和代码放在一起会更方便于参考,读者可以自行决定使用哪种方式。这里将会给出一个缺少部分细节的高级说明;如果想要了解更多 Rust 知识,请查看在线书籍 The Rust Programming Language

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
第一行就像在说“哇 Rust,我们在用一个叫做 wasm_bindgen 的库”。
在 Rust 当中,库被称为“crates”,因为我们使用的是一个外部库,所以有 "extern"。
明白了吗? Cargo ships crates.
*/
extern crate wasm_bindgen;

/*第三行包括了一个将库中的代码引入到你的代码中的使用命令。
在这个情况下,将会引入 wasm_bindgen::prelude 的全部模块。
我们将在下一节中使用这些内容。 */
use wasm_bindgen::prelude::*;

/*
在 #[] 中的内容叫做 "属性",并以某种方式改变下面的语句。(其实有点类似于C#的属性)
#[wasm_bindgen] 后面跟着的语句是一个 extern,它将告诉 Rust 这下面的函数将由外部提供,我们想调用一些外部定义的函数。
而前面的这个属性标注告诉我们 这些函数将由 "wasm-bindgen" 来进行导入。(类似于 import {alert} from "wasm-bindgen")

这里的 alert 是 js 中的一个函数,它可以打开一个消息通知窗口,参数s为str类型,用于指示显示的内容。

当你想调用新的 JavaScript 函数时,你可以在这里写他们,wasm-bindgen 将负责为你设置一切。

并非一切都得到支持,但我们正在努力!
*/
#[wasm_bindgen]
extern {
pub fn alert(s: &str);
}

/*
这里的 #[wasm_bindgen] 和上面的类似,但是这里,#[wasm_bindgen] 后面 跟着的不再是 extern,而是 pub fn。

这代表我们希望能够在 JavaScript 中使用这个 Rust 函数。
这和 extern 正相反:我们并非引入函数,而是要把函数给外部世界使用。(类比到js里面就是 export {greet})

这个函数的名字是 greet,它需要一个参数,一个字符串(写作 &str)。
它调用了我们前面在 extern 块中引入的 alert 函数。
它传递了一个让我们串联字符串的 format! 宏的调用。

*/
#[wasm_bindgen]
pub fn greet(name: &str) {
/*
format! 在这里有两个参数,一个格式化字符串和一个要填入的变量。
格式化字符串是 "Hello, {}!" 部分。
它可以包含一个或多个 {},变量将会被填入其中。

传递的变量是 name,也就是这个函数的参数。
所以当我们调用 greet("Steve")时我们就能看到 "Hello, Steve!"。
*/
alert(&format!("Hello, {}!", name));
}

把我们的代码编译到 WebAssembly

为了能够正确的编译我们的代码,首先我们需要配置 Cargo.toml。打开这个文件,将内容改为如下所示:

Cargo.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

你需要改为自己的仓库,同时 Cargo 需要通过 git 来完善 authors 部分。

最重要的是添加底下的部分。第一个部分 — [lib] — 告诉 Rust 为我们的包建立一个 cdylib 版本;在本教程中我们不会讲解它的含义。有关更多信息,请参阅 CargoRust Linkage 文档。

第二个部分是 [dependencies] 部分。在这里我们告诉 Cargo 我们需要依赖哪个版本的 wasm-bindgen ;在这个例子中,它是 0.2.z 版本的 (不是 0.3.0 或者其他版本)。

构建包

现在我们已经完成了所有配置项,开始构建吧!在命令行输入以下命令:

1
wasm-pack build --scope mynpmusername

或者

1
wasm-pack build

上面的方式会附带你的签名。

这个命令将做一系列事情 (这会花一些时间,特别是当你第一次运行 wasm-pack)。想了解详细情况,查看这篇在 Mozilla Hacks 上的文章。简单来说,wasm-pack build 将做以下几件事:

  1. 将你的 Rust 代码编译成 WebAssembly。
  2. 在编译好的 WebAssembly 代码基础上运行 wasm-bindgen,生成一个 JavaScript 文件将 WebAssembly 文件包装成一个模块以便 npm 能够识别它。
  3. 创建一个 pkg 文件夹并将 JavaScript 文件和生成的 WebAssembly 代码移到其中。
  4. 读取你的 Cargo.toml 并生成相应的 package.json
  5. 复制你的 README.md (如果有的话) 到文件夹中。

最后的结果?你在 pkg 文件夹下有了一个 npm 包。

报错了?对咯!

教程的说明就截止到上面了,但是大概率,你报错了。

MDN的文章毕竟面对的是全球的开发者,但是在国内有一个比较烦的问题就是网络的访问,在执行wasm-pack build的时候,它会这样报错:

1
2
3
4
5
6
7
8
9
10
E:\_Project\Rust\hello-wasm>wasm-pack build
[INFO]: 🎯 Checking for the Wasm target...
[INFO]: 🌀 Compiling to Wasm...
warning: no edition set: defaulting to the 2015 edition while the latest is 2024
Compiling hello-wasm v0.1.0 (E:\_Project\Rust\hello-wasm)
Finished `release` profile [optimized] target(s) in 0.99s
Error: failed to download from https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-windows.tar.gz
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.
Caused by: failed to download from https://github.com/WebAssembly/binaryen/releases/download/version_117/binaryen-version_117-x86_64-windows.tar.gz
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.

这里它说是无法下载 wasm-opt 这个东西,但是实际上我直接通过链接是能够直接下载的,网上说更改代理能够解决问题,我这里没有尝试这种方式,但是我还是将这种方法贴在这里:

如果你尝试通过cargo 去提前下载好 wasm-opt 的话,就会遇到另外一个问题,它一会给你报错无法找到c++编译器,但是实际上我的电脑里面安装了vs2019,所以说实际上根本不应该存在这个问题。当我将vs2019的cl.exe添加到环境变量之后,它又会报错说无法找到clang,之后我又去llvm的官网下载了clang,但是它又报错说c++ 'algorithm' file not found,这个问题我解决不了,暂时也不知道是为什么,这让我浪费了很多时间。

对于新学者来说,最好的办法就是按照它的提示的后者,将wasm-opt = false添加到你的Cargo.toml文件中,这样就可以跳过这个步骤,直接编译你的WebAssembly模块。wasm-opt 是一个优化工具,它会对你的WebAssembly模块进行优化,但是实际上,这个优化工具并不是必须的,你可以直接跳过这个步骤,直接编译你的WebAssembly模块。

对了,还有一个问题,就是它虽然叫你需要在 package metadata 中添加wasm-opt = false,但是实际上,你并不知道在哪里添加,需要翻阅官方文档:Cargo.toml Configuration - Hello wasm-pack!

Cargo.toml 配置

wasm-pack 可以通过 Cargo.toml 中的 package.metadata.wasm-pack 键进行配置。 每个选项都有一个默认值,不是必需的。

共有三个配置文件:devprofilingrelease。 这些对应于传递给 wasm-pack build--dev--profiling--release 标志。

可用的配置选项及其默认值如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[package.metadata.wasm-pack.profile.dev]
# Should `wasm-opt` be used to further optimize the wasm binary generated after
# the Rust compiler has finished? Using `wasm-opt` can often further decrease
# binary size or do clever tricks that haven't made their way into LLVM yet.
#
# Configuration is set to `false` by default for the dev profile, but it can
# be set to an array of strings which are explicit arguments to pass to
# `wasm-opt`. For example `['-Os']` would optimize for size while `['-O4']`
# would execute very expensive optimizations passes
wasm-opt = ['-O']

[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
# Should we enable wasm-bindgen's debug assertions in its generated JS glue?
debug-js-glue = true
# Should wasm-bindgen demangle the symbols in the "name" custom section?
demangle-name-section = true
# Should we emit the DWARF debug info custom sections?
dwarf-debug-info = false

[package.metadata.wasm-pack.profile.profiling]
wasm-opt = ['-O']

[package.metadata.wasm-pack.profile.profiling.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = false

# `wasm-opt` is on by default in for the release profile, but it can be
# disabled by setting it to `false`
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[package.metadata.wasm-pack.profile.release.wasm-bindgen]
debug-js-glue = false
demangle-name-section = true
dwarf-debug-info = false

通过翻阅文档,我们知道,我们需要在package.metadata.wasm-pack.profile.release中添加wasm-opt = false,这样就可以跳过这个步骤了。 修改后的Cargo.toml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"

[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

对代码体积的一些说明

如果你检查生成的 WebAssembly 文件体积,它可能有几百 kB。我们没有让 Rust 去压缩生成的代码,从而大大减少生成包的体积。这和本次教程主题无关,但如果你想了解更多,查看 Rust WebAssembly 工作组文档上关于 减少 .wasm 体积 的说明。

把我们的包发布到 npm

把我们的新包发布到 npm registry:

1
2
cd pkg
npm publish --access=public

我们现在有了一个 npm 包,使用 Rust 编写,但已经被编译为 WebAssembly 了。 现在这个包已经可以被 JavaScript 使用了,而且使用它完全不需要用户安装 Rust;包中的代码是 WebAssembly 代码,而不是 Rust 源码!

在网站上使用我们的包

让我们建立一个使用我们包的网站!人们通过各种打包工具使用 npm 包,在本教程中,我们将使用 vite。它比其他某些打包工具要更为的便捷,但是你可以使用任何你喜欢的工具。

从这里开始,将会和MDN的教程有所不同,因为我没有使用webpack,而是使用vite,所以说这里将会给出使用vite的教程。

让我们离开pkg目录,并创建一个新目录site,尝试以下操作:

1
2
3
cd ../..
mkdir site
cd site

创建一个新文件 package.json,然后输入如下代码:

1
2
3
4
5
6
7
8
9
10
11
{
"scripts": {
"dev": "vite",
},
"dependencies": {
"@mynpmusername/hello-wasm": "^0.1.0",
"vite": "^2.6.0",
"vite-plugin-top-level-await": "^1.5.0",
"vite-plugin-wasm": "^3.4.1"
}
}

如果上面你并没有将自己的包发布到npm上,那么你需要手动将上一步生成的pkg文件夹拷贝到site文件夹下,然后在package.json中将dependencies中的@mynpmusername/hello-wasm改为file:./pkg

这样它将会从本地的pkg文件夹中引入你的WebAssembly模块。

如果上面你将自己的包发布到npm上了,那么请注意,你需要在依赖项部分的 @ 之后填写自己的用户名。

接下来,我们需要配置 Vite 来使用 WebAssembly。创建一个新文件 vite.config.js,并输入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { defineConfig } from 'vite'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
plugins: [
wasm(),
topLevelAwait()
],
optimizeDeps: {
exclude: ['hello_wasm'],
}
})

这里的optimizeDeps是为了解决一个问题,就是vite会将你的WebAssembly模块当作一个模块来处理,但是实际上它并不是一个模块,所以说我们需要将它排除在外,防止vite对它进行处理,使得模块找不到对应的文件。

vite-plugin-wasm 是一个 Vite 插件,它会帮助你加载 WebAssembly 模块。vite-plugin-top-level-await 是一个 Vite 插件,它会帮助你使用顶层 await。

接下来,我们需要一个 HTML 文件。创建一个index.html并写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>hello-wasm example</title>
</head>
<body>
hello-wasm
<button id="testButton">Test Button</button>
<script type="module" src="./index.js"></script>
</body>
</html>

需要注意的是,MDN 里面使用的是<script src="./index.js"></script>,但是实际上,vite是不支持这种方式的,你需要使用<script type="module" src="./index.js"></script>这种方式来引入你的js文件。

这样的话,你可以在index.js中直接使用import {greet} from '@mynpmusername/hello-wasm'来引入你的WebAssembly模块,而不需要额外的处理。

现在我们来写index.js文件:

1
2
3
4
5
6
7
8
9
import * as wasm from "hello-wasm";

wasm.greet("WebAssembly");
//debug
console.debug("loaded");
document.querySelector("#testButton").addEventListener("click", () => {
console.debug("clicked button");
wasm.greet("WebAssembly");
});

加载后,它将从该模块调用greet函数,并传入字符串“WebAssembly”参数。注意这里看上去没有什么特别的,但是我们正在调用 Rust 代码!就 JavaScript 代码所知,这只是一个普通模块。

我们已经完成了所有的文件!让我们试一下:

1
2
npm install
npm run dev

这将启动一个 Web 服务器。访问 vite 给出的链接,比如,http://localhost:5178/ 你应该会在屏幕上看到一个内容为 Hello, WebAssembly! 的警告框。点击按钮时也会看到这个警告框。

我们已经成功地从 JavaScript 调用了 Rust,并从 Rust 调用了 JavaScript。

结论

本教程到此结束。希望你觉得它有用。

非常感谢来自 Rust 和 WebAssembly 社区的所有人,他们的工作使得这一切成为可能。 同样也非常感谢这位作者,他的文章非常通俗易懂,且提供了github上的代码仓库,让我们可以直接下载代码进行测试: 我使用 vue3 + WebAssembly 做了个文件校验网站,性能提升600%_vue webassembly-CSDN博客 jethroHuang/fast_hash

它的这里面还使用了worker来进一步提高性能,这里就不再赘述了,有兴趣的可以去看看。关于worker的内容,可以参考这篇文章:Web Workers API - Web APIs | MDN

阅读全文 »

# electron

Tauri 初体验:前途可期

之前一直苦恼于 electron 打包出来的体积太大,总是想要使其的体积更小,对于使用者更加友好。

最近发现了一个新的框架:Tauri,它是一个基于 Rust 的框架,可以将 Rust 代码编译成一个可执行文件,然后将这个可执行文件和前端代码打包成一个可执行文件。

本身我就对 Rust 比较感兴趣,所以就尝试了一下。

但是结果就是……Tauri的开发体验简直是糟糕。

简陋的开始

一开始使用一个简单的命令就可以创建一个基础的项目:

1
tauri create my-app

在选择了一些配置项之后,就可以运行这个项目了:

1
2
cd my-app
tauri dev

这个命令会启动一个开发服务器,然后打开一个窗口,显示你的应用。

一步步走到这里,一切都顺利,但是问题是:然后呢?

下面是它的文档的目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-   快速开始

- [什么是 Tauri?](https://v2.tauri.org.cn/start/)
- [先决条件](https://v2.tauri.org.cn/start/prerequisites/)
- [创建项目](https://v2.tauri.org.cn/start/create-project/)
- 前端配置

- [概述](https://v2.tauri.org.cn/start/frontend/)
- [Leptos](https://v2.tauri.org.cn/start/frontend/leptos/)
- [Next.js](https://v2.tauri.org.cn/start/frontend/nextjs/)
- [Nuxt](https://v2.tauri.org.cn/start/frontend/nuxt/)
- [Qwik](https://v2.tauri.org.cn/start/frontend/qwik/)
- [SvelteKit](https://v2.tauri.org.cn/start/frontend/sveltekit/)
- [Trunk](https://v2.tauri.org.cn/start/frontend/trunk/)
- [Vite](https://v2.tauri.org.cn/start/frontend/vite/)

- 升级与迁移

- [概述](https://v2.tauri.org.cn/start/migrate/)
- [从 Tauri 1.0 升级](https://v2.tauri.org.cn/start/migrate/from-tauri-1/)
- [从 Tauri 2.0 Beta 升级](https://v2.tauri.org.cn/start/migrate/from-tauri-2-beta/)

在配置完前端(我使用的是vite)之后呢?让我们继续看看下面的非快速开始的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
-   核心概念

- [概述](https://v2.tauri.org.cn/concept/)
- [Tauri 架构](https://v2.tauri.org.cn/concept/architecture/)
- [进程模型](https://v2.tauri.org.cn/concept/process-model/)
- [应用大小](https://v2.tauri.org.cn/concept/size/)
- 进程间通信

- [概述](https://v2.tauri.org.cn/concept/inter-process-communication/)
- [棕地模式](https://v2.tauri.org.cn/concept/inter-process-communication/brownfield/)
- [隔离模式](https://v2.tauri.org.cn/concept/inter-process-communication/isolation/)


- 安全性

- [概述](https://v2.tauri.org.cn/security/)
- [权限](https://v2.tauri.org.cn/security/permissions/)
- [命令作用域](https://v2.tauri.org.cn/security/scope/)
- [功能](https://v2.tauri.org.cn/security/capabilities/)
- [内容安全策略 (CSP)](https://v2.tauri.org.cn/security/csp/)
- [HTTP 标头 新](https://v2.tauri.org.cn/security/http-headers/)
- [Tauri 生态系统安全](https://v2.tauri.org.cn/security/ecosystem/)
- [应用程序生命周期威胁](https://v2.tauri.org.cn/security/lifecycle/)
- [未来工作](https://v2.tauri.org.cn/security/future/)
- [运行时权限](https://v2.tauri.org.cn/security/runtime-authority/)

- 开发

- [概述](https://v2.tauri.org.cn/develop/)
- [从 Rust 调用前端](https://v2.tauri.org.cn/develop/calling-frontend/)
- [从前端调用 Rust](https://v2.tauri.org.cn/develop/calling-rust/)
- [配置文件](https://v2.tauri.org.cn/develop/configuration-files/)
- [嵌入其他文件](https://v2.tauri.org.cn/develop/resources/)
- [嵌入外部二进制文件](https://v2.tauri.org.cn/develop/sidecar/)
- [状态管理](https://v2.tauri.org.cn/develop/state-management/)
- [更新依赖项](https://v2.tauri.org.cn/develop/updating-dependencies/)
- 调试

- [概述](https://v2.tauri.org.cn/develop/debug/)
- [CrabNebula DevTools 新](https://v2.tauri.org.cn/develop/debug/crabnebula-devtools/)
- [在 Neovim 中调试](https://v2.tauri.org.cn/develop/debug/neovim/)
- [在 JetBrains IDE 中调试](https://v2.tauri.org.cn/develop/debug/rustrover/)
- [在 VS Code 中调试](https://v2.tauri.org.cn/develop/debug/vscode/)

- 插件

- [概述](https://v2.tauri.org.cn/develop/plugins/)
- [移动插件开发](https://v2.tauri.org.cn/develop/plugins/develop-mobile/)

- 测试

- [概述](https://v2.tauri.org.cn/develop/tests/)
- [模拟 Tauri API](https://v2.tauri.org.cn/develop/tests/mocking/)
- WebDriver

- [概述](https://v2.tauri.org.cn/develop/tests/webdriver/)
- [持续集成](https://v2.tauri.org.cn/develop/tests/webdriver/ci/)
- 示例

- [设置](https://v2.tauri.org.cn/develop/tests/webdriver/example/)
- [Selenium](https://v2.tauri.org.cn/develop/tests/webdriver/example/selenium/)
- [WebdriverIO](https://v2.tauri.org.cn/develop/tests/webdriver/example/webdriverio/)

- 分发

- [概述](https://v2.tauri.org.cn/distribute/)
- ……后面略去

进程间通信这里,它在概述里面介绍了它使用的是类似于 IPC 的方式,但是问题是具体的调用方式在哪里呢?它没有举例子,而文档直到后面的 从 Rust 调用前端 才有介绍。

而且它也没讲解入口文件是哪个?主进程和渲染进程中主进程是在哪个文件中启动的?

模糊的文档

更为好笑的是:

1
import {invoke} from '@tauri-apps/api/tauri'

这个示例用的代码无法运行,因为它找不到这个模块。为什么找不到呢?因为它安装tauri的时候并不默认包含它的api,而是需要手动安装:

1
npm install @tauri-apps/api

欸,你认为这就结束了吗?不,它的文档也没有提到这个模块到底是哪里的,它在:

1
import {invoke} from '@tauri-apps/api/core'

这个模块中。而不是教程中的 @tauri-apps/api/tauri

初次之外,类似的事情还在文件系统上,在文档中它说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 文件系统

[GitHub](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/fs) [npm](https://npmjs.net.cn/package/@tauri-apps/plugin-fs) [crates.io](https://crates.io/crates/tauri-plugin-fs)

API 参考[](https://v2.tauri.org.cn/reference/javascript/fs/)[](https://docs.rs/tauri-plugin-fs)

访问文件系统。

## [支持的平台](https://v2.tauri.org.cn/plugin/file-system/#_top#supported-platforms)

_此插件需要 Rust 版本至少为 **1.77.2**_

| 平台 | 级别 | 注意 |
| --- | --- | --- |
| windows | | 通过 MSI 或 NSIS 安装在 `perMachine` 和 `both` 模式下的应用,需要在 `$RESOURCES` 文件夹中进行写入访问时需要管理员权限|
| linux | | 无法写入访问 `$RESOURCES` 文件夹|
| macos | | 无法写入访问 `$RESOURCES` 文件夹|
| android | | 默认情况下,访问权限仅限于应用程序文件夹 |
| ios | | 默认情况下,访问权限仅限于应用程序文件夹|

但是在另外一处,它说:

1
2
# 文件系统
fs模块可以使用 window.__TAURI__.fs 来访问文件系统。

但是实际上这种方式是无法使用的,因为它需要先安装 @tauri-apps/api/fs,而不是 @tauri-apps/plugin-fs

欸,但是 2.0 版本的 fs 模块已经被默认移除了,需要使用 plugin-fs。

## 缺失的功能

我写的程序为了优化性能,使用了creatlink而非copy,但是在tauri中并没有这个功能,所以我需要自己写一个rust后端来实现这个功能并且还需要处理多平台的问题,要么就是仍然使用 nodejs 作为依赖。

但是这样的话,tauri的优势就没有了,因为我还是需要依赖 nodejs。

……

总结

只能说未来可期,但是现在的体验真的是太差了,文档不全,不清晰,而且还有很多坑,不建议使用。

不过rust的编程的思路很有意思,虽然说编程的时候碍手碍脚的,但是因为其稳定性和性能,还是值得一试的。

阅读全文 »

# AI绘画

Day 2:提示词与参数优化

学习目标:掌握提示词编写技巧,调整参数提升生成质量。 - 实践步骤: 1. 提示词公式:使用“主体+细节+风格+质量”结构(如“a knight in armor, detailed armor texture, dark fantasy style, 8k, masterpiece”)。 2. 权重控制:用(keyword:1.5)增强权重,[keyword]降低权重。 3. 参数调整: - 采样方法:推荐DPM++ 2M Karras(速度快且稳定)。 - 迭代步数:20-30步(平衡速度与质量)。 - 提示词相关性(CFG Scale):7-12(值越高越贴近描述,但可能过锐化)。 - 检验成果:生成一张符合复杂提示词描述的图片(如“科幻城市夜景,赛博朋克风格”)。

权重调整方法

权重控制在咒语中非常重要,这关系着你想要的东西是否合乎心意的获得了AI的重视。

最基础的权重控制为控制prompt在咒语中的位置。越靠前的词汇越受到重视。

权重控制可以通过对prompt加括号进行。() 或 {} 为加强权重,[] 为减弱权重,括号可以一次扩多个词条。(1girl:1.5)括号后对添加 ":1.5"是对词条直接赋予权重,数字即为权重大小。数字越大权重越大,默认1,通常为0-2之间。

目前在webui中()为对其中内容权重乘以1.1,[]为对其中内容除以1.1,而{}在webui版本中不生效。虽然多重括号也能生效,但是这种方法低效且不优雅,例如((((girl))))进行了4次括号,但是只对girl权重增加到了1.4641。

这里强烈推荐使用(prompt:权重数)的方式来进行权重调整。

选中一组词,然后按方向键上下(根据编译器版本不同可能是ctrl+方向键)可以直接完成权重调整。

预制提示词测试

这里,大佬给了一个非常棒的风格模板,后面加上想要的提示词大概率能出非常好的图像效果:

00086-1533094715.png

1
2
3
(masterpiece, extremely detailed 8k wallpaper,best quality),(best illumination, best shadow, extremely delicate and beautiful),dynamic angle,floating,finely detail,Depth of field (bloom),(shine),glinting stars,classic,(illustration),(painting),(sketch),1girl,solo,bare shoulders,medium breasts,areola,red eyes,beautiful detailed cold face,black hair,straight_hair,short hair,extremely delicate and beautiful girls,beautiful detailed eyes,(((rose))),cage,bandage,(red rope),  
Negative prompt: lowres,bad anatomy,bad hands,text,error,missing fingers,extra digit,fewer digits,cropped,worst quality,low quality,normal quality,jpeg artifacts,signature,watermark,username,blurry,sketches,EasyNegativeV2,badhandv4,
Steps: 18, Sampler: DPM++ 2M, Schedule type: Karras, CFG scale: 7, Seed: 1533094715, Size: 512x512, Model hash: 7f96a1a9ca, Model: anything-v5, Denoising strength: 0.7, Clip skip: 2, Hires upscale: 2, Hires upscaler: Latent, TI hashes: "EasyNegativeV2: 339cc9210f70, badhandv4: 5e40d722fc3d", Version: v1.10.1

权重调整策略

阅读全文 »

# AI绘画

Day 1:安装环境与基础生成

学习目标:完成Stable Diffusion本地部署,生成第一张AI图片。 - 实践步骤: 1. 下载秋葉整合包(解压密码:bilibili@秋葉aaaki),安装.NET依赖项,双击启动器一键运行。 2. 打开WebUI界面(默认地址http://127.0.0.1:7860),输入简单提示词(如“1 girl, masterpiece”),生成测试图。 3. 熟悉界面功能:模型切换、提示词输入区、生成参数(采样方法、迭代步数)。 - 检验成果:成功生成一张基础图片(如猫或人物)。 - 学习资料: - 秋葉整合包安装教程:CSDN博客。 - 界面功能速查表(见下文)。

阅读全文 »

# 未完成
# AI绘画

我从今天开始正式立一个小的目标要开始入门学习AI绘画入门,这一篇文章主要是作为提纲挈领的作用,用于为我自己明确目标并且梳理学习的路径。

最终目标:

达到类似于这样的效果:

嗯,我对于背景没有太大的要求,上面的图来自于这两个账号:

(13) さがわ/sagawa (@sagawa_gawa) / X (13) lilium (@lilydisease) / X

虽然这几张图都有或多或少的问题,但是对于光影的把控已经非常到位了,对于肉感也有非常好的刻画。

目标拆分:

我现在想要将我的目标拆分为几个小的目标,但是我对这个并不熟悉,所以我希望使用ai帮我规划一下学习路线:

1
2
3
4
5
6
7
8
9
10
你好,现在请你扮演一位对于AI绘画拥有丰富了解的学姐,现在我希望你能够帮助我入门ai绘画,我拥有 4060ti16G 以及 48G的内存,对于ai绘画的入门应该够用了。
我现在的目标是:
1. 能够使用ai绘画为我生成指定内容的图片
2. 能够通过某种方式来调整图片生成的画风
3. 能够使用通过某种方式来精准的控制画面中的角色(比如说我自己设定的角色或者其他动漫游戏作品中的角色)
4. 能够通过某种方式精确的控制画面中的场景以及人物的动作

我知道我上面的目标的描述可能会有些模糊,这是因为毕竟对于ai绘画来说是一个门外汉,我希望你能够帮我梳理并总结我的目标,我现在正在计划按照秋葉aaaki的教程来进行学习,但是这位大佬并没有给出详细的学习路线,我希望我能够在一周的时间内快速入门并且达到上述目标,我每天能够抽出至少2个小时的时间用于学习这方面的知识。另外,我认为最好的学习方式就是实践,希望你能够尽可能让我在实践的过程中学习。

我希望你能够针对上述我的目标以及学习计划,为我规划一条路线清晰的学习路线,告诉我每个阶段的学习资料,学习目标,如何检验我的学习成果,非常感谢!

DeepSeek的回答: ### AI绘画入门学习路线规划(7天实践版) 硬件说明:你的配置(RTX 4060Ti 16G显存 + 48G内存)完全满足Stable Diffusion本地部署需求,可高效运行模型和插件。


学习目标与阶段拆解

根据你的需求,目标可总结为: 1. 基础生成:能通过文本描述生成指定内容的图片。 2. 风格控制:调整画面风格(如动漫、写实、油画等)。 3. 角色控制:精准生成特定角色(自定义或已有IP)。 4. 场景与动作控制:控制构图、人物动作、场景细节。

学习核心工具:秋葉aaaki整合包(Stable Diffusion WebUI中文版)+ ControlNet插件 + LoRA模型。


每日学习计划

Day 1:安装环境与基础生成

[[AI绘画入门学习-第一天]] 学习目标:完成Stable Diffusion本地部署,生成第一张AI图片。 - 实践步骤: 1. 下载秋葉整合包(解压密码:bilibili@秋葉aaaki),安装.NET依赖项,双击启动器一键运行。 2. 打开WebUI界面(默认地址http://127.0.0.1:7860),输入简单提示词(如“1 girl, masterpiece”),生成测试图。 3. 熟悉界面功能:模型切换、提示词输入区、生成参数(采样方法、迭代步数)。 - 检验成果:成功生成一张基础图片(如猫或人物)。 - 学习资料: - 秋葉整合包安装教程:CSDN博客。 - 界面功能速查表(见下文)。


Day 2:提示词与参数优化

[[AI绘画入门学习-第二天]] 学习目标:掌握提示词编写技巧,调整参数提升生成质量。 - 实践步骤: 1. 提示词公式:使用“主体+细节+风格+质量”结构(如“a knight in armor, detailed armor texture, dark fantasy style, 8k, masterpiece”)。 2. 权重控制:用(keyword:1.5)增强权重,[keyword]降低权重。 3. 参数调整: - 采样方法:推荐DPM++ 2M Karras(速度快且稳定)。 - 迭代步数:20-30步(平衡速度与质量)。 - 提示词相关性(CFG Scale):7-12(值越高越贴近描述,但可能过锐化)。 - 检验成果:生成一张符合复杂提示词描述的图片(如“科幻城市夜景,赛博朋克风格”)。 - 学习资料: - 提示词手册(附常用风格关键词):CSDN资源包


Day 3:风格控制与模型管理

学习目标:切换不同大模型(底模)和LoRA插件调整画风。 - 实践步骤: 1. 下载模型:从Civitai下载风格化大模型(如动漫风Anything V5、写实风Realistic Vision)。 2. 加载模型:将.safetensors文件放入models/Stable-diffusion文件夹,在WebUI中切换。 3. 使用LoRA:下载角色/风格LoRA模型(如Genshin Impact角色LoRA),在提示词中添加触发词(如<lora:genshin_zhongli:0.8>)。 - 检验成果:用同一提示词生成不同风格的图片(如“古风少女”分别用动漫和写实模型输出)。 - 学习资料: - 模型管理教程:SD WebUI文档


Day 4:精准控制角色(ControlNet + LoRA)

学习目标:通过ControlNet插件固定角色形象,结合LoRA定制角色。 - 实践步骤: 1. 安装ControlNet:在秋葉整合包中启用插件,下载预处理器和模型文件。 2. 线稿控制:上传角色线稿,启用ControlNetCannyLineart模式,生成与线稿一致的角色。 3. 训练自定义LoRA:使用Kohya脚本训练个人角色(需准备20-30张角色多角度图片)。 - 检验成果:生成与线稿或自定义LoRA匹配的角色(如“我的自设角色,蓝色长发,机械臂”)。 - 学习资料: - ControlNet教程:B站视频


Day 5:场景与动作控制(ControlNet进阶)

学习目标:利用ControlNet控制构图、人物姿势、场景深度。 - 实践步骤: 1. 姿势控制:使用OpenPose预处理器,上传姿势参考图,生成相同动作的角色。 2. 场景控制:用Depth模式输入场景深度图,控制画面层次感。 3. 分镜提示词:结合分镜描述(如“人物站在悬崖边,左手持剑,右手指向远方”)。 - 检验成果:生成指定动作和场景的图片(如“骑士骑马冲锋,背景是雪山”)。 - 学习资料: - ControlNet高级用法:ComfyUI教程


Day 6:综合实战与优化

学习目标:结合所有技能生成复杂作品,优化细节。 - 实践步骤: 1. 设计完整场景(如“赛博朋克酒吧,女黑客坐在吧台,霓虹灯闪烁”)。 2. 分阶段调整:先用ControlNet固定构图,再通过提示词细化细节(服装、光影)。 3. 使用高清修复(Hires. fix)提升分辨率(2倍放大)。 - 检验成果:输出一张高完成度的综合作品(含角色、场景、风格)。


Day 7:扩展学习与效率提升

学习目标:探索高级插件(如动态扩散、工作流优化)。 - 推荐方向: - 动态模型转换:生成连续动作帧(如转身、行走)。 - 批量生成:用XYZ脚本生成多组参数对比图。 - 效率工具:安装Tiled Diffusion插件减少显存占用。


学习资料汇总

类型 资源链接 用途
整合包 秋葉整合包下载 本地部署SD
提示词 AIGC提示词手册 风格与细节控制
模型库 Civitai 下载大模型与LoRA
进阶教程 ControlNet全解 精准控制画面

检验学习成果的标准

  • 基础生成:能通过提示词生成无明显崩坏的图片。
  • 风格控制:同一主题可输出3种以上不同风格(如油画、像素风、水墨)。
  • 角色控制:生成与线稿或LoRA匹配的角色(误差<10%)。
  • 场景与动作:构图符合预设,人物动作自然无畸形。

按此路线实践,一周后可熟练运用AI绘画工具完成创作需求。遇到问题可随时查阅秋葉整合包的社区讨论(B站/CSDN),或私信我进一步解答! 🎨

阅读全文 »

# 药剂工艺

游戏本质上是一个模拟经营类型的游戏,不过,它将制作魔药包装成了在地图上通过搭配路径走迷宫的方式,使得游戏增加了非常大的趣味,再加上本身十分浓厚的模拟属性,让人似乎真的觉得自己是作为一个炼药师,为大家炼药。

游戏的美术独具特色,整体的视效和音效都打磨的非常的好。

但是在游戏的后期,仍然存在不少的问题: 1. 驱动力不足:在游戏的后期,驱动力严重不足,因为做出来的材料并没有基于玩家正反馈,做出的黑化、白化之类的物品似乎只是

阅读全文 »

# 未完成
#

键盘维修

键盘的部分按键老化了不灵敏了,打算再去买一罐新的轴体,为了保持手感的一致,这里还是选择使用金粉轴,下面记录一下有问题的轴:

阅读全文 »

���������???, �����������???.
阅读全文 »
0%