初次体验Tauri和Sycamore (2)

原创作者:庄晓立(LIIGO)
原创时间:2025年2月8日(首次发布时间)
原创链接:https://blog.csdn.net/liigo/article/details/145520637
版权所有,转载请注明出处。
关键词:Sycamore, Tauri, Dioxus, Leptos, Rust, WebAssembly, Reactive, JSX, React, Web

tauri-splash

前言

Tauri 2.0发布于2024年10月2日,Sycamore 0.9发布于2024年11月1日。二者在近期双双发布重大版本升级,是我(LIIGO)这次想体验他们的主要动机。Tauri自2022年发布v1.0之后就早已火出天际,而Sycamore自2022发布v0.8之后沉寂了两年之久,如今各自凤凰涅槃,他们的组合体会擦出怎样的火花?

这是上一篇体验Tauri 2.0的姊妹篇。本文重点是体验Sycamore 0.9。

关于Sycamore

Sycamore, a library that makes it effortless to write performant user interfaces using the power of fine-grained reactivity. Sycamore uses open web standards such as WebAssembly to run your Rust code on the web, an environment that has been traditionally dominated by JavaScript.

Sycamore库提供细粒度响应式能力,可轻松编写高性能UI应用。Sycamore使用WEB标准技术WebAssembly等,将你的Rust代码运行在WEB中(传统上此类应用由JavaScript主导)。

Sycamore使用类似于JSX的声明式DSL语言描述UI,并提供组件、事件、路由、双向数据绑定等功能。

Sycamore和Tauri的关系

Tauri负责整个GUI应用框架,Sycamore在框架中负责前端UI渲染。Sycamore也可以脱离Tauri独立工作,用于开发基于WASM的WEB前端应用。

创建App

本节内容主要摘抄自同系列文章第一篇《初次体验Tauri和Sycamore (1)》。

Tauri使用两个命令行工具 (create-tauri-app, tauri-cli) 创建和编译打包App。首先要安装这两个CLI:

cargo install create-tauri-app --locked
cargo install tauri-cli --version "^2.0.0" --locked

create-tauri-app, tauri-cli 从Rust源码编译(cargo install)都相当耗时(均依赖数百个crates)。我还是建议自行下载编译后版本放到cargo bin目录。我给他们提了建议,今后会推荐使用 cargo binstall 下载编译好的二进制CLI,而不是使用cargo install从源码开始编译。

这两个CLI也有都对应的npm包:create-tauri-app, @tauri-apps/cli,二者都是间接调用Rust编译好的可执行文件。供TS/JS前端使用。

执行如下命令开始创建Tauri app:cargo create-tauri-app。CLI会逐步引导你输入或选择如下信息:

  • 项目名称(Project name),默认是"tauri-app"
  • Identifier 默认是"com.tauri-app.app"
  • 前端语言,可选 Rust, TS/JS, .Net
  • UI模板,视前端语言而定
    • Rust UI模板:可选 Vanilla, Yew, Leptos, Sycamore, Dioxus
    • TS/JS UI模板:可选 Vanilla, Vue, Svelte, React, Solid, Angular, Preact
    • .Net UI模板:可选 Blazor

选TS/JS的UI模板前还需要选择包管理器:pnpm, yarn, npm, deno, bun

因为这次我(Liigo)想体验Tauri+Sycamore,因而前端语言选Rust,UI模板选Sycamore。

目录结构

Tauri+Sycamore App目录结构:

├─ public/
├─ src/
│  ├─ app.rs
│  └─ main.crs
├─ src-tauri/
│  ├─ ...
│  ├─ Cargo.toml
│  └─ tauri.conf.json
├─ .gitignore
├─ .taurignore
├─ Cargo.toml
├─ index.html
├─ README.md
├─ styles.css
└─ Trunk.toml

Tauri源码目录对前端和后端代码进行了隔离。后端代码使用src-tauri/子目录;前端代码使用除此之外的其他文件和子目录。

编译打包

本节内容主要摘抄自同系列文章第一篇《初次体验Tauri和Sycamore (1)》。

开发版

cargo tauri dev

编译完成后自动启动App,弹出GUI主窗口。允许开发者在App运行过程中修改前端源代码,Tauri(或者说Trunk)会自动编译,并刷新App窗口内容,但是App并不会中途退出或重启(原理:Trunk通过WebSocket向开发版App推送重新加载UI的指令;Dioxus虽然没用Trunk但也实现了类似机制)。

它会检查Trunk是否存在,不存在的话会自动下载源码并编译。但是在Windows下编译Trunk很可能会碰到如下问题(间接依赖openssl开发者库):

  It looks like you're compiling for MSVC but we couldn't detect an OpenSSL
  installation. If there isn't one installed then you can try the rust-openssl
  README for more information about how to download precompiled binaries of
  OpenSSL:

  https://github.com/sfackler/rust-openssl#windows

  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: failed to compile `trunk v0.21.2`, intermediate artifacts can be found at `E:\tmp\RUST_DIR\CARGO_TARGET_DIR`.
To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path.

我找到的解决办法是,去github的trunk官方仓库下载编译好的trunk.exe,丢进cargo bin目录即可(或任意PATH目录均可)。

如果cargo tauri dev过程中看到如下提示时只需耐心等待:

Warn Waiting for your frontend dev server to start on http://localhost:1420/…

这是因为Trunk先启动了(WEB服务监听1420端口),等待APP主动连接。但是编译App需要时间,等它编译完并启动后才能连上。

通过App窗口右键菜单"检查"可以打开devtools。在App运行过程中,还可以在浏览器中打开 http://localhost:1420/ ,网页外观和功能跟App窗口是一样的(可视为App的另一个实例)。

发行版

cargo tauri build

编译App并打包为安装包。

如果编译过程中提示正在下载Wix但失败(Github国内连接不稳定):

Downloading https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip

你可以通过其他方法手动下载此连接,解压到如下目录:C:\Users\liigo\AppData\Local\tauri\WixTools314\(里面有一堆exe等文件)。

此方法是我(LIIGO)从 Tauri仓库源码 里扒出来的。实证管用。

同理,如果NSIS也下载不了,可以用类似的办法手动下载解压到目录C:\Users\liigo\AppData\Local\tauri\NSIS。但是我没用这个方法。因为我觉得,既然已经有Wix用来生成MSI安装包,就没必要再下载NSIS用来生成另一种安装包。我研究了一下,将配置文件tauri.conf.json里面的bundle.targets改为"msi"(原来是"all")即可禁用NSIS等。

文件大小

Tauri+Sycamore app发行版编译后是一个可独立运行的图形用户界面(GUI)exe,其内部整合了wasm/css/图片等文件,没有其他外部依赖。exe文件大小是10.3MB,对应的安装包msi文件大小是3.6MB(安装后也只有那个exe和一个用于卸载的快捷方式文件(指向系统文件msiexec.exe /x))。Sycamore生成的wasm文件大小为750KB(已包含在exe中)。App启动时有大约一两秒的窗口白屏。

作为对比,再看一下Tauri+Dioxus app的数据:exe大小10.6MB,msi大小3.9MB,wasm文件大小为1.3MB(debug版33MB或25MB),也有一两秒的启动白屏。大同小异吧。我(LIIGO)暂且认为这是Tauri App (Hello world)的平均水平。

这样的文件大小应该很香吧。最起码比Electron app香多了。

1MB的wasm文件,用在普通网站上,网络传输加载延迟是一个较大的负担,但是对Tauri app这种桌面应用而言,就是本地加载啊,性能没得说。况且Tauri还应用了"localhost free"技术,直接注入Webview,连本地WEB传输步骤也省了。

Instead of relying on localhost server that expose your frontend to all other processes we use native webview apis to inject the assets right before the webview requests hit the network stack. – FabianLars

资源占用

Tauri+Sycamore app发行版启动后,内部加载3到6个Webview2进程,连同exe合计占用内存60到90MB。

无操作时CPU占用率为0%;在app窗口上移动鼠标时,CPU占用率逐步上升到10%甚至更多。这个问题是不是需要改善呀。

20241230 LIIGO补记:在VsCode中打开Tauri+Sycamore app项目源码,VsCode大概占用2.5GB内存甚至更多。

Tauri+Dioxus app的表现与之类似。

前端代码全览

以下是Tauri CLI生成的前端核心文件src\app.rs全部代码,先整体感受一下Sycamore。

其主体代码定义了一个App组件(App函数)。App组件内部负责描述UI、管理状态、处理事件。

use serde::{Deserialize, Serialize};
use sycamore::futures::spawn_local_scoped;
use sycamore::prelude::*;
use sycamore::web::events::SubmitEvent;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
    async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}

#[derive(Serialize, Deserialize)]
struct GreetArgs<'a> {
    name: &'a str,
}

#[component]
pub fn App() -> View {
    let name = create_signal(String::new());
    let greet_msg = create_signal(String::new());

    let greet = move |e: SubmitEvent| {
        e.prevent_default();
        spawn_local_scoped(async move {
            // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
            let args = serde_wasm_bindgen::to_value(&GreetArgs {
				name: &name.get_clone()
			})
			.unwrap();
            let new_msg = invoke("greet", args).await;
            greet_msg.set(new_msg.as_string().unwrap());
        })
    };

    view! {
        main(class="container") {
            h1 {
                "Welcome to Tauri + Sycamore"
            }

            div(class="row") {
                a(href="https://tauri.app", target="_blank") {
                    img(src="public/tauri.svg", class="logo tauri", alt="Tauri logo")
                }
                a(href="https://sycamore.dev", target="_blank") {
                    img(src="public/sycamore.svg", class="logo sycamore", alt="Sycamore logo")
                }
            }
            p {
                "Click on the Tauri and Sycamore logos to learn more."
            }

            form(class="row", on:submit=greet) {
                input(id="greet-input", bind:value=name, placeholder="Enter a name...")
                button(r#type="submit") {
                    "Greet"
                }
            }
            p {
                (greet_msg)
            }
        }
    }
}

声明式UI

Sycamore使用view!()宏描述UI,大致类似于React系的JSX语言。

语法上相对比较陌生。虽然本质上还是写HTML,但并未直接采用HTML或JSX语法,也未直接采用Rust语法,它最终呈现给我们的是一种糅合了函数调用和花括号子块的形式(form(attr1, attr2) { sub-nodes })。内部支持///* */注释。

view! {
    h1 {
        "Welcome to Tauri + Sycamore"
    }
    form(class="row", on:submit=greet) {
        input(id="greet-input", bind:value=name, placeholder="Enter a name...")
        button(r#type="submit") {
            "Greet"
        }
    }
    p {
        (greet_msg)
    }
}

在前端声明式UI语法上,不同的作者有不同的喜好倾向。LIIGO本人的感受是,Leptos更像JSX,Dioxus更像Rust,Sycamore则介于二者中间。主要区别是风格口味,暂且不谈孰好孰坏吧。总之它们都可以统一归结为“类JSX”或者“神似JSX”学派。

组件

在Sycamore中创建定义组件是非常简单的。组件有几个特征:普通的函数,添加#[component]标注,返回值类型固定为View,函数体主体是view!宏调用,里面是类JSX语法。组件函数内部view!前面,可以定义状态变量,可以写Rust代码;view!内部也可以嵌入Rust表达式;参见后文状态管理一节。

#[component]
pub fn Hello() -> View {
    view! {
        div {
            "hello"
        }
    }
}

我照猫画虎把前面app.rs里面的一部分UI提取为一个独立组件,welcome组件:

#[component]
fn welcome() -> View {
    view! {
        h1 {
            "Welcome to Tauri + Sycamore"
        }
        div(class="row") {
            a(href="https://tauri.app", target="_blank") {
                img(src="public/tauri.svg", class="logo tauri", alt="Tauri logo")
            }
            a(href="https://sycamore.dev", target="_blank") {
                img(src="public/sycamore.svg", class="logo sycamore", alt="Sycamore logo")
            }
        }
        p {
            "Click on the Tauri and Sycamore logos to learn more."
        }
    }
}

调用端语法示例:

#[component]
pub fn App() -> View {
    view! {
        self::welcome {}
    }
}

welcome前面必须加上self::前缀,否则编译报错:

error[E0425]: cannot find function welcome in module sycamore::rt::tags

看样子它固定去sycamore::rt::tagsmodule里加载组件,无视welcome就在当前module里。希望后续改进。


组件当然也可以有属性(Attributes),附官方示例定义代码和调用代码。

#[component(inline_props)]
fn Button(
    // `html` means that we are spreading onto an HTML element.
    // The other possible value is `svg`.
    //
    // `button` means that we are spreading onto a `<button>` element.
    #[prop(attributes(html, button))]
    attributes: Attributes,
    // We can still accept children.
    children: Children,
    // We can still accept other props besides `attributes`.
    other_prop: i32,
) -> View {
    view! {
        // The spread (`..xyz`) syntax applies all the attributes onto the element.
        button(..attributes)
    }
}
view! {
    Button(
        // Pass as many HTML attributes/events as you want.
        id="my-button",
        class="btn btn-primary",
        on:click=|_| {}
        // You can still pass in regular props as well.
        other_prop=123,
    ) {
        // Children still gets passed into the `children` prop.
        "Click me"
    }
}

状态管理

使用create_signal等函数创建状态对象,例如:

let name = create_signal(String::new());
let greet_msg = create_signal(String::new());

显示状态对象的值(小括号括住):

view! {
    p {
        (greet_msg)
    }
}

文本内插值:"ok " (greet_msg) " yes"。状态值必须写在常量文本双引号外面,严格来说是伪内插值;Leptos也类似(小括号改为花括号);Dioxus支持"ok {greet_msg} yes"真内插值。

更新状态对象的值(Signal<T>::set):

greet_msg.set(new_value);

双向绑定到组件属性(属性value⇄状态name),语法:bind:属性=状态,示例:

view! {
    input(id="greet-input", bind:value=name)
}

事件

以下示例代码处理FORM组件的submit事件,其中事件处理函数greet是一个闭包函数(有一个event参数,类型取决于具体事件):

let greet = move |e: SubmitEvent| {
    // ...
};

view! {
    form(class="row", on:submit=greet) {
        // ...
    }
}

事件处理函数通常是被写成组件函数内部的闭包函数形式,这是因为事件处理函数内往往需要访问状态对象(Signal<T>),而状态对象往往是组件函数内部的局部变量,在组件函数内部定义的闭包函数访问组件函数内的局部变量是很方便的(自动捕获闭包外部变量)。

但是这种形式把逻辑代码和UI代码放在一个组件函数里,很容易导致大函数缝合怪,一大坨事件处理代码 + 一大坨UI描述代码(view!{}),互相喧宾夺主。我感觉组件函数的主体应该是view!{}(加上少量状态对象定义代码),理想情况下应该把事件处理代码隔离出去。

事件处理代码当然也可以写成独立函数的形式(独立于组件函数),我(LIIGO)专门做了一些尝试并且也成功了(不知道能不能作为最佳实践)。首先要解决Signal对象不在当前作用域的问题,需要通过参数传递Signal,可是有了Signal参数这函数就不可能符合事件处理函数的原型(有且只有一个Event参数)。我的做法是让这个函数执行后再返回符合事件处理函数原型的闭包函数。代码如下,mygreet()为独立函数,调用mygreet()后返回事件处理函数:

#[component]
pub fn App() -> View {
    let name = create_signal(String::from("LIIGO"));
    let greet_msg = create_signal(String::new());
    view! {
        form(class="row", on:submit=mygreet(name, greet_msg)) {
            // ...
        }
    }
}

fn mygreet(name: Signal<String>, greet_msg: Signal<String>) -> impl Fn(SubmitEvent) {
    move |e| {
        let new_msg = invoke("greet", args).await;
        greet_msg.set(new_msg.as_string().unwrap());
    }
}

整改后的app.rs

以下是我整改后的app.rs文件全部代码,主要是拆解、抽象、隔离,但保持原有功能不变。

use serde::{Deserialize, Serialize};
use sycamore::futures::spawn_local_scoped;
use sycamore::prelude::*;
use sycamore::web::events::SubmitEvent;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
    async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}

#[derive(Serialize, Deserialize)]
struct GreetArgs<'a> {
    name: &'a str,
}

#[component]
pub fn App() -> View {
    let name = create_signal(String::from("LIIGO"));
    let greet_msg = create_signal(String::new());

    view! {
        main(class="container") {
            self::welcome {}

            form(class="row", on:submit=mygreet(name, greet_msg)) {
                input(id="greet-input", bind:value=name, placeholder="Enter a name...")
                button(r#type="submit") {
                    "Greet"
                }
            }
            p {
                (greet_msg)
            }
        }
    }
}

fn mygreet(name: Signal<String>, greet_msg: Signal<String>) -> impl Fn(SubmitEvent) {
    move |e| {
        print!("{}", name.get_clone());
        e.prevent_default();
        spawn_local_scoped(async move {
            // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
            let args = serde_wasm_bindgen::to_value(&GreetArgs {
                name: &name.get_clone()
            })
            .unwrap();
            let new_msg = invoke("greet", args).await;
            greet_msg.set(new_msg.as_string().unwrap());
        })
    }
}

#[component]
fn welcome() -> View {
    view! {
        h1 {
            "Welcome to Tauri + Sycamore"
        }
        div(class="row") {
            a(href="https://tauri.app", target="_blank") {
                img(src="public/tauri.svg", class="logo tauri", alt="Tauri logo")
            }
            a(href="https://sycamore.dev", target="_blank") {
                img(src="public/sycamore.svg", class="logo sycamore", alt="Sycamore logo")
            }
        }
        p {
            "Click on the Tauri and Sycamore logos to learn more."
        }
    }
}

Sycamore / Leptos / Dioxus

这三者有诸多相似性:

  • 都具备响应式WEB功能
  • 都提供类似于JSX的前端UI描述语言
  • 都支持UI组件化
  • 都是用Rust编程语言开发的开源项目
  • 都支持编译为WebAssembly(WASM)在浏览器内运行
  • 都可以作为Rust前端被集成进Tauri 2.0 App

当然它们也都可以脱离Tauri各自独立开发WEB应用(其中Dioxus还可以开发桌面应用和移动应用)。

三者之中目前只有Dioxus支持热加载(Hot Module Reloading, HMR),修改UI代码后无需重新编译即时预览结果,开发体验更佳。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/967056.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

模型压缩 --学习记录2

模型压缩 --学习记录2 如何找到更好的权衡方式(模型量化)方法一:寻找更好的 range方法二:寻找更好的 X-fp32(浮点数)方法三:寻找更好的 scale 和 zp方法四:寻找更好的 roundPTQ 后训练量化(离线量化)QAT 量化感知训练(在线量化)量化为什么会带来加速?三、模型稀疏技…

Vue与Konva:解锁Canvas绘图的无限可能

前言 在现代Web开发中&#xff0c;动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js&#xff0c;作为一款轻量级且高效的前端框架&#xff0c;凭借其响应式数据绑定和组件化开发模式&#xff0c;赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js&#xff0c;两者结…

8.JVM-方法区

前言 这次所讲述的是运行时数据区的最后一个部分 从线程共享与否的角度来看 ThreadLocal&#xff1a;如何保证多个线程在并发环境下的安全性&#xff1f;典型应用就是数据库连接管理&#xff0c;以及会话管理 栈、堆、方法区的交互关系 下面就涉及了对象的访问定位 Person&a…

面向对象程序设计-实验3

题目1 &#xff08;给出题目描述&#xff09;设计一个类CRectangle 代码清单&#xff1a; #include<iostream> using namespace std; class CRectangle { public: CRectangle() { m_l1.0; m_w1.0; } void get() { cin>>m_l; if(m_l>50) { m_l1.0; } cin&g…

边缘计算网关驱动智慧煤矿智能升级——实时预警、低延时决策与数字孪生护航矿山安全高效运营

迈向智能化煤矿管理新时代 工业物联网和边缘计算技术的迅猛发展&#xff0c;煤矿安全生产与高效运营正迎来全新变革。传统煤矿监控模式由于现场环境复杂、数据采集和传输延时较高&#xff0c;已难以满足当下高标准的安全管理要求。为此&#xff0c;借助边缘计算网关的实时数据…

deepseek本地部署+web图形化页面配置+对比其他ai模型

公众号&#xff1a;泷羽Sec-尘宇安全 前言 最近deepseek非常火&#xff0c;训练成本低&#xff0c;其预训练费用仅为OpenAI GPT-4o模型的不到十分之一&#xff0c;但是效果堪比OpenAI&#xff0c;使用深度思考&#xff0c;回答的问题很不错&#xff0c;啥都敢说 可惜惨遭ddos…

Deepseek 接入Word处理对话框(隐藏密钥)

硅基流动邀请码&#xff1a;1zNe93Cp 邀请链接&#xff1a;网页链接 亲测deepseek接入word&#xff0c;自由调用对话&#xff0c;看截图有兴趣的复用代码&#xff08;当然也可以自己向deepseek提问&#xff0c;帮助你完成接入&#xff0c;但是提问逻辑不一样给出的答案是千差万…

从家庭IP到全球网络资源的无缝连接:Cliproxy的专业解决方案

数字化时代&#xff0c;家庭IP作为个人或家庭接入互联网的门户&#xff0c;其重要性日益凸显。然而&#xff0c;要实现从家庭IP到全球网络资源的无缝连接&#xff0c;并享受高效、安全、稳定的网络访问体验&#xff0c;往往需要借助专业的代理服务。Cliproxy&#xff0c;作为业…

微信点餐系统小程序ssm+论文源码调试讲解

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

牛客寒假集训营2

A 牛客传送门 代码如下: const int N2e610,M1e410; const int INF0x3f3f3f3f; const int mod998244353; ll n;void solve(){map<int,int>mp;mp[1]mp[2]mp[3]mp[5]mp[6]1;for(int i1;i<7;i){int a…

第3章 使用 Vue 脚手架

第3章 使用 Vue 脚手架 3.1 初始化脚手架3.1.1 说明3.1.2. 具体步骤3.1.3 分析脚手架结构1 总结2 细节分析1 配置文件2 src文件1 文件结构分析2 例子 3 public文件4 最终效果 3.2 ref属性3.3 props配置项3.4 mixin混入3.5 插件3.6 scoped样式3.7 Todo-list 案例3.7.1 组件化编码…

去除install4j学习版生成的安装程序和主程序的neg弹窗的解决思路

文章目录 去除install4j学习版生成的安装程序和主程序的neg弹窗的解决思路概述笔记打补丁之前打补丁之后 效果备注 END 去除install4j学习版生成的安装程序和主程序的neg弹窗的解决思路 概述 最近可能有修改openpnp源码并打包的需求。 openpnp2.2 用 install4j 10.0.5 来打包…

前后端服务配置

1、安装虚拟机&#xff08;VirtualBox或者vmware&#xff09;&#xff0c;在虚拟机上配置centos(选择你需要的Linux版本)&#xff0c;配置如nginx服务器等 1.1 VMware 下载路径Sign In注册下载 1.2 VirtualBox 下载路径https://www.virtualbox.org/wiki/Downloads 2、配置服…

基于javaweb的SpringBoot+MyBatis毕业设计选题答辩管理系统(源码+文档+部署讲解)

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 运行环境开发工具技术框架适用功能说明eclipse/MyEclipse运行&#xff1a; ![在这里插入图片描述](https://i-blog.csdnimg.cn/dir…

【Elasticsearch】nested聚合

在 Elasticsearch 中&#xff0c;嵌套聚合&#xff08;nestedaggregation&#xff09;的语法形式用于对嵌套字段&#xff08;nestedfields&#xff09;进行聚合操作。嵌套字段是 Elasticsearch 中的一种特殊字段类型&#xff0c;用于存储数组中的对象&#xff0c;这些对象需要独…

Linux第106步_Linux内核RTC驱动实验

1、了解rtc_device结构体 1)、打开“include/linux/rtc.h” rtc_class_ops是需要用户根据所使用的RTC设备编写的,其结构体如下: struct rtc_class_ops { int (*ioctl)(struct device *, unsigned int, unsigned long);/*函数指针ioctl*/ int (*read_time)(struct device *,…

微信小程序案例2——天气微信小程序(学会绑定数据)

文章目录 一、项目步骤1 创建一个weather项目2 进入index.wxml、index.js、index.wxss文件,清空所有内容,进入App.json,修改导航栏标题为“中国天气网”。3进入index.wxml,进行当天天气情况的界面布局,包括温度、最低温、最高温、天气情况、城市、星期、风行情况,代码如下…

Linux系统-centos防火墙firewalld详解

Linux系统-centos7.6 防火墙firewalld详解 1 firewalld了解 CentOS 7.6默认的防火墙管理工具是firewalld&#xff0c;它取代了之前的iptables防火墙。firewalld属于典型的包过滤防火墙或称之为网络层防火墙&#xff0c;与iptables一样&#xff0c;都是用来管理防火墙的工具&a…

Sealos的k8s高可用集群搭建

Sealos 介绍](https://sealos.io/zh-Hans/docs/Intro) Sealos 是一个 Go 语言开发的简单干净且轻量的 Kubernetes 集群部署工具&#xff0c;能很好的支持在生产环境中部署高可用的 Kubernetes 集群。 Sealos 特性与优势 支持离线安装&#xff0c;工具与部署资源包分离&#…

算法篇——动态规划

核心思想&#xff1a; 将问题分解为重叠的子问题&#xff0c;并储存子问题的解&#xff08;使用字典、数组或哈希表&#xff09;&#xff0c;避免重复计算&#xff0c;从而提高效率。 题目特点&#xff1a;重叠子问题&#xff08;特殊地&#xff0c;是最优子结构&#xff09; …