基于trunk、yew构建web开发脚手架

trunk 构建、打包 rust wasm 程序;yewweb 前端开发库;

项目仓库yew-web

trunk

之前已经简单介绍了trunk,全局安装:

$> cargo install --locked trunk

常用命令:

  • trunk build 基于wasm-bindgen构建 wasm 程序。
  • trunk watch 检测文件系统,更改时触发新的构建
  • trunk serve 和 trunk watch 动作一致,创建一个 web 服务
  • trunk clean 清理之前的构建
  • trunk config show 显示当前 trunk 的配置
  • trunk tools show 打印出项目中 trunk 需要的工具

新增配置文件Trunk.toml,可以通过trunk tools show查看需要的工具,它们的下载的地址。包括sass\tailwindcss\wasm-bindgen\wasm-opt

安装yewweb 开发工具,类似 react 设计;安装之前文章介绍的gloo,它是js_sys\web_sys的二次分装,简化对于 web api、js 语法的使用。还有链接 web 前端不可或缺的wasm-bindgen

[dependencies]
gloo = "0.11.0"
wasm-bindgen = "0.2.92"
yew = { version = "0.21.0", features = ['csr'] }

正常的页面开发,路由必不可少,用以跳转不同的页面。基于yew的路由库yew-router

安装:

$> cargo add yew-router

在代码中,我们默认就只使用函数组件一种方式书写#[function_component]

文件目录介绍:

  • public 静态资源目录,打包时直接拷贝到编译目录下。
  • src/assets 资源目录,图片、css 等
  • src/routes 路由
  • src/stores 共享数据
  • src/views 视图文件定义
  • src/main.rs 入口文件,根文件。
  • src/app.rs 主视图文件

所有的模块定义,我们在其根目录下定义mod.rs文件统一声明导出,然后在main.rs中声明为全局的 crate,这样任何目录下想要访问其他目录则可以通过根包导入使用。

main.rs,导入了src/routes路由根包;导入了src/views视图文件根包;还有主视图文件app.rs

mod app;
mod routes;
mod views;
//
use app::App;fn main() {yew::Renderer::<App>::new().render();
}

然后在app.rs中,导入了主路由文件route,组件BrowserRouter做为根路由器,会注册一个路由上下文,可在全局所有组件内访问。

use yew::prelude::*;
use yew_router::prelude::*;// 主路由文件
use crate::routes::route::{switch, Route};#[function_component]
pub fn App() -> Html {html! {<BrowserRouter><Switch<Route> render={switch} /></BrowserRouter>}
}

链接资源加载

trunk 中所有的链接资源必遵循三个原则:

  1. 必须声明有效的 html link标记。
  2. 必须有属性data-trunk
  3. 必须有res={type}属性,type则为其支持的资源类型

trunk目前支持的资源 type - rust 、sass/scss 、css 、tailwind-css、icon、inline 、copy-file、copy-dir

我们将public直接复制整个目录到打包目录中。

<link data-trunk rel="copy-dir" href="public" />

trunk 使用dart-sass来打包编译scss文件,我们在assets/base.scss定义样式,然后在index.html加载

<link data-trunk rel="scss" href="/src/assets/base.scss" />

base.scss作为基础样式资源,其他模块的样式文件,可以全部导入到这这个文件.

trunk-import-scss.png

可以互相依赖,trunk暂时没有提供方案处理每个页面的样式,只能以外部引入的方式处理。持续关注,有关的提案还在讨论中。

脚本资源加载

脚本资源加载有三个原则:

  1. 必须声明为有效的 htmlscript标记
  2. 必须有属性 data-trunk
  3. 必须有属性src,指向脚本文件

这允许我们可以直接导入一些js脚本,trunk 会复制到 dist 目录中,就可以在程序中使用它们。通过哈希处理以便进行缓存。

服务基本路径baseURI

可以在Trunk.toml中配置服务的基本路径,

[build]
# public_url = "/hboot/"
public_url = ""

如果配置了基本路由,那我们在程序里的路径处理就会多一个前缀/hboot/,想要在程序里访问这个路径,则需要配置index.html<head>增加:

<base data-trunk-public-url />

这对于路由管理非常重要,不然就匹配不到设置的路径。在运行程序中可以通过document.baseURI访问。

trunk 构建过程

  1. 读取并解析index.html文件
  2. 生成所有资源编译计划
  3. 并行构建所有资源
  4. 构建完成将资源写入暂存目录
  5. 将 html 文件写入暂存目录
  6. 将 dist 目录内容替换为暂存目录的内容

未来可能发生变化。

material_yew ui 库

适配yew设计的 Material Design 风格的 UI 库。

material_yew;

引入发现不兼容,这里先占个坑位。

使用原生的input \ button组件构建页面,再加一点样式。

use std::ops::Deref;use web_sys::{Event, HtmlInputElement};
use yew::prelude::*;#[function_component]
pub fn App() -> Html {let name = use_state(|| "admin".to_string());let update_name = {let name = name.clone();Callback::from(move |event: Event| {let input = event.target_dyn_into::<HtmlInputElement>();if let Some(input) = input {name.set(input.value())}})};return html! {<input class="hb-input" value={name.clone().deref().to_string()} οnchange={update_name} required={true} name={"name"} type={"text"} placeholder={"用户名"} />}
}

yew-router 定义路由

src/routes/route.rs定义主路由

use yew::prelude::*;
use yew_router::prelude::*;// 路由配对的视图文件
use crate::views::main;
use crate::views::not_found;#[derive(Clone, Routable, PartialEq)]
pub enum Route {#[at("/")]Main,#[not_found]#[at("/404")]NotFound,
}// 作为Switch 组件的属性render绑定;回调处理匹配的路由应该渲染什么
pub fn switch(routes: Route) -> Html {match routes {Route::Main => html! {<main::App />},Route::NotFound => html! {<not_found::App />},}
}

定义枚举值Route,再与组件<Switch />配对,组件通过路径查找将其传递给render回调,在回调中决定渲染什么。如果没有匹配的路由,则会匹配到具有#[not_found]属性的路径。若未指定则什么都不渲染。

src/app.rs导入路由,上面已经贴过代码<Switch<Route> render={switch} />

src/views定义了main以及not_found视图文件,启动访问http://127.0.0.1:8080/

yew-route.png

use_navigator 获取路由导航对象

从一个页面跳转到另一个页面,通过use_navigator获取导航对象。

use crate::routes::route::Route;#[function_component]
pub fn App() -> Html {let navigator = use_navigator().unwrap();let handle_logout = {Callback::from(move |_| {navigator.push(&Route::Login);})};
}

包含了常用跳转路由的方法push \ go \ back \ forward等。

除了手动跳转路由,通过Link组件点击触发路由跳转。比如我们来增加左侧的菜单,切换不同的视图。

// ...#[function_component]
pub fn App() -> Html {html! {<div class="hb-nav-menu"><div class="nav-menu"><Link<MainRoute> classes={"menu-item"} to={MainRoute::Home}>{"首页"}</Link<MainRoute>><Link<MainRoute> classes={"menu-item"} to={MainRoute::User}>{"用户"}</Link<MainRoute>></div></div>}
}

嵌套路由

我们使用Redirect重定向路由,比如访问/重定向到/main

#[derive(Clone, Routable, PartialEq)]
pub enum Route {#[at("/")]Home// ...
}pub fn switch(routes: Route) -> Html {// log!(JsValue::from(routes));match routes {Route::Home => html! {<Redirect<MainRoute> to={MainRoute::Home}/>},// ...}
}

嵌套路由,之前在根组件中使用Switch处理路由,我们创建/routes/main.rs处理带有顶部导航栏、左侧菜单的路由.

// ...other#[derive(Clone, Routable, PartialEq)]
pub enum MainRoute {#[at("/main")]Home,#[at("/main/user")]User,#[not_found]#[at("/main/404")]NotFound,
}pub fn main_switch(routes: MainRoute) -> Html {match routes {MainRoute::Home => html! {<h1>{"首页"}</h1>},MainRoute::User => html! {<user::App />},MainRoute::NotFound => html! {<Redirect<Route> to={Route::NotFound} />},}
}

同样的,在这个子路由中也存在NotFound,匹配不到时我们重定向到根路由Route::NotFound404 页面。在这里Home \ User就代表了左侧的两个菜单。

在根路由中/routes/route.rs,定义了/main /main/*的匹配路由,它们都指向渲染main::App组件

#[derive(Clone, Routable, PartialEq)]
pub enum Route {#[at("/main")]MainRoot,#[at("/main/*")]Main,// ...
}pub fn switch(routes: Route) -> Html {// log!(JsValue::from(routes));match routes {// ...Route::Main | Route::MainRoot => html! {<main::App />}}
}

我们需要在main::App页面中使用Switch分发路由

// ...
use crate::routes::main::{main_switch, MainRoute};#[function_component]
pub fn App() -> Html {html! {<div class="hb-main"><header::App></header::App><div class="hb-main-layout"><nav_menu::App></nav_menu::App><div class="hb-main-content"><Switch<MainRoute> render={main_switch} /></div></div></div>}
}

yew-route-nested.png

use_route 当前路由信息

use_location 获取当前路由信息,比use_route信息多一点,同History::location

当路由发生变化时,当前组件会重新渲染。

之前加了左侧菜单,需要处理下点击时添加对活动菜单的样式,增加类active

#[function_component]
pub fn App() -> Html {// ...html! {<div class="hb-nav-menu"><div class="nav-menu"><Link<MainRoute> classes={is_active(MainRoute::Home)} to={MainRoute::Home}>{"首页"}</Link<MainRoute>><Link<MainRoute> classes={is_active(MainRoute::User)} to={MainRoute::User}>{"用户"}</Link<MainRoute>></div></div>}
}

Link组件接受classes定义类,我们定义一个is_active方法去处理当前的链接是否处于活动状态。参数为当前渲染的路由枚举值。

let is_active = |route: MainRoute| {let mut class = classes!("menu-item");let menu = active_menu.clone();if route == *menu {class.push("active");}class
};

is_active 中,默认每个链接都有menu-item,然后通过active_menu变量判断是否需要追加类active

定义active_menu,通过路由的变化,来变更活动的路由值。使用了use_effect_withhook 接受route作为依赖,变更时触发调用,然后通过active_menu_set更新值。

// 当前路由
let route: Option<_> = use_route::<MainRoute>();let active_menu = use_state(|| MainRoute::Home);
let active_menu_set = active_menu.clone();use_effect_with(route.clone(), move |_| {// ..let route = route.clone();if let Some(active_route) = route {active_menu_set.set(active_route);}
});

yew-route-active-menu.png

跨组件数据交互

父子组件之间可以通过 props 进行数据传递。跨组件如果采用这种一层层传就很冗余,更加麻烦不好管理。

通过使用到数据的组件跟组件上挂载上下文 context,然后子组件消耗使用。通过ContextProvider包裹根部元素

将所有的数据模块放在目录/src/stores,新建了一个app.rs定义数据App

#[derive(Clone, Debug, PartialEq)]
pub struct App {pub name: String,
}

在视图主文件中app.rs引入并初始化数据,使用ContextProvider传递。

#[function_component]
pub fn App() -> Html {let app = use_state(|| app::App {name: "yew-web".to_string(),});html! {<BrowserRouter><ContextProvider<app::App> context={(*app).clone()} ><Switch<Route> render={switch} /></ContextProvider<app::App>></BrowserRouter>}
}

在所有的子孙组件通过use_context钩子函数获取消费使用。

use yew::prelude::*;use crate::stores::app;#[function_component]
pub fn App() -> Html {let context = use_context::<app::App>().unwrap();html! {<div class="user"><h2>{"个人中心"}</h2><p>{"消费来自根部组件的数据:"}{context.name}</p></div>}
}

跨组件数据更新

除了消费使用,可能还需要更新,我们在顶部栏加一个按钮处理更新。只有数据来源都在同一个地方,那么我们通过use_reducer来更新数据。数据变更后,根组件触发重新渲染,再向下传递。

/stores/app定义AppProvider,并初始化数据,这里通过使用use_reducer初始化了数据,向下传递的app是带有dispatch方法的,可以用来更新数据。

// 自定义上下文数据类型
pub type AppContext = UseReducerHandle<App>;#[derive(Properties, Debug, PartialEq)]
pub struct AppProviderProps {#[prop_or_default]pub children: Html,
}#[function_component]
pub fn AppProvider(props: &AppProviderProps) -> Html {let app = use_reducer(|| App {name: "yew-web".to_string(),});html! {<ContextProvider<AppContext> context={app}>{props.children.clone()}</ContextProvider<AppContext>>}
}

我们向下传递的 context 是使用use_reducer创建的,对于ContextProvider需要的类型,通过自定义类型AppContext。在子孙组件使用,则需导入使用app::AppContext

修改视图主文件,可以直接使用app::AppProvider包裹根组件:

#[function_component]
pub fn App() -> Html {// let app = use_reducer(|| app::App {//     name: "yew-web".to_string(),// });html! {<BrowserRouter>// <ContextProvider<app::App> context={(*app).clone()} >//     <Switch<Route> render={switch} />// </ContextProvider<app::App>><app::AppProvider><Switch<Route> render={switch} /></app::AppProvider></BrowserRouter>}
}

yew-context-update.gif

网络请求

网络请求必不可少,请求后端数据完成页面渲染。

通过现有封装的依赖库完成数据请求,包括:

  • gloo-net http 请求库
  • serde 高效的数据序列化、反序列化框架
  • wasm-bindgen-futures 提供了在 rust 和 js 之间的异步交互能力

安装后测试请求数据并渲染到页面

定义了接口响应数据结构TopicResponse,主体数据结构Topic:

// 数据主体
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct Topic {pub id: String,pub title: String,pub top: bool,pub visit_count: i32,pub content: String,pub create_at: String,
}// 请求响应
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct TopicResponse {pub success: bool,pub data: Vec<Topic>,
}

发起请求,使用use_effect_with接受依赖变更时触发,这里我们这调用一次:

use gloo_net::http::Request;
use wasm_bindgen_futures::spawn_local;#[function_component]
pub fn App() -> Html {let data = use_state(|| vec![]);let data_set = data.clone();use_effect_with((), move |_| {spawn_local(async move {let data: TopicResponse = Request::get("https://****/api/v1/topics").send().await.unwrap().json().await.unwrap();data_set.set(data.data);});});// ...
}

在这里使用到了新语法async...await..., 它可以让我们以同步的方式书写异步代码。通过async声明的块、函数或闭包,会返回一个Future类型,它不会阻塞当前线程的执行,当它处于.await时事件循环会将控制权交给其他任务。在它被挂起的时候,他执行的上下文也会被保存。

注意 rsut 的版本,async...await在稳定本^1.39可用

Requestgloo_net提供的请求模块,它是原生fetch的包装,以便我们更方便的调用。

我们在这里调用没有传参数,我们可以定义接口需要的请求参数,首先定义请求数据结构:

/*** 请求参数*/
#[derive(Serialize, Debug, Clone, PartialEq)]
pub struct TopicRequest {pub page: i32,pub tab: String,pub limit: i32,pub mdrender: bool,
}

然后在视图文件中,创建请求数据实例,我们让use_effect_with依赖实例,当参数变化时,重新发出请求

#[function_component]
pub fn App() -> Html {// reqlet params = use_state(|| TopicRequest {page: 1,tab: "good".to_string(),limit: 10,mdrender: false,});use_effect_with(params.clone(), move |req_params| {let req = req_params.clone();spawn_local(async move {let page = req.page.to_string();let limit = req.limit.to_string();let tab = req.tab.to_string();let mdrender = req.mdrender.to_string();let query = vec![("page", &page),("limit", &limit),("tab", &tab),("mdrender", &mdrender),];let data: TopicResponse = Request::get("https://****/api/v1/topics").query(query).send().await.unwrap().json().await.unwrap();data_set.set(data.data);});});// ...
}

Request::get调用返回gloo_net::http::RequestBuilder实例,通过query()方法添加请求参数,暂时需要挨个组装一个下参数,没有找到直接转换的方法。如果是post可以使用body()

然后在页面上增加按钮,切换上一页、下一页。增加事件监听:

let update_params = params.clone();
let handle_jump_next = {let req_set = update_params.clone();Callback::from(move |_| {req_set.set(TopicRequest {page: update_params.page + 1,..(*update_params).clone()});})
};

我们只更新了page参数,其他值不做修改,通过*update_params解引用获取到值。..指定剩余未显示设置值的字段与给定实例相同的值.

在所有的代码逻辑中没有处理接口请求失败、或者响应数据错误的问题。这里假设我们一切都请求正常,通过data去渲染视图:

html! {<div class="user"><ul class="list">{data.iter().map(move |item|{html! {<li key={format!("{}", item.id)}><p>{format!("{}", item.title)}</p></li>}}).collect::<Html>()}</ul></div>
}

使用web_sys Request 请求

gloo-net是基于web_sys的底层 fetch API 的二次封装。它是基于 rust 的异步特性构建的,使用async/await处理异步请求,可以跨平台,不止处理 web 平台。

相比于gloo-net,web_sys更倾向 web 平台,它提供了浏览器原生 API 的抽象。在 web Assembly 环境中于 web Api 交互。

通过使用web_sys提供的原生 fetch 处理异步请求,开发上面列表的详情页面detail

在列表页面数据各项增加点击事件,事件处理并跳转至详情页面

let info = item.clone();
let navigator = navigator.clone();let view_detail = Callback::from(move |_| {let id = info.id.clone();navigator.push(&MainRoute::CNodeDetail { id: id });
});
html! {<li key={format!("{}", &item.id)} οnclick={view_detail}><p>{format!("{}", &item.title)}</p></li>
}

在详情页面获取到传过来的参数id,然后发起请求获取当前文章的详情信息,

先看一下 js 原生 api fetch 的请求示例

fetch("https://****/api/v1/topic/*id").then((res) => {return res.json();}).then((res) => {// 接口响应console.log(res);});

使用web_sys提供的 api 调用的步骤基本一致,我们通过提供的window()函数获取到 js 全局Window对象,然后调用请求方法fetch_with_str(),还有另一个fetch_with_request()参数需要使用Request初始化构造请求参数。

详情页面需要列表点击传过来的参数id,使用use_effect_with增加依赖项,为了保证回调函数cb不被清理,保证它存在一个很长的生命周期,使用了forget()方法。

use web_sys::{window};#[function_component]
pub fn App(props: &DetailProps) -> Html {// ...use_effect_with(query_id, move |query_id| {let url = format!("https://****/api/v1/topic/{}", *(query_id.clone()));info!("{}", url);let window = window().unwrap();let _ = window.fetch_with_str(&url).then(&cb);|| cb.forget()});// ...
}

看一下方法fetch_with_str签名fn fetch_with_str(&self, input: &str) -> Promise,传参为接口地址 url,响应一个Promise对象,通过Promise的方法来解析响应数据。这里我们调用了then()方法接受响应。

看下Promise的 then 方法的签名fn then(&self, cb: &Closure<dyn FnMut(JsValue)>) -> Promise 它接受一个实现了FnMuttrait 的闭包函数的引用,闭包参数为JsValue类型的数据。

来定义这个回调闭包函数,通过 Closure::wrap()创建一个闭包,并转换为实现了FnMuttrait 的 trait 对象;使用了智能指针Box::new来创建Box类型的实例,因为响应数据不知道其大小、也不确定其生命周期。

let cb = Closure::wrap(Box::new(move |value: JsValue| {let res = value.dyn_into::<Response>().unwrap();let json = res.json().unwrap();let _ = json.then(&get_data);
}) as Box<dyn FnMut(JsValue)>);

根据 js 原生 fetch 调用,第一次的then方法返回的是一个Response类型,我们需要把JsValue转换为Response类型,通过JsCast::dyn_into()方法处理 js 与 rust 之间的数据转换。然后就可以调用json()方法了,解析响应并再次通过then()方法处理响应数据

这里接收到的就是我们接口具体的数据响应了,定义数据结构:

#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct DetailResponse {pub success: bool,pub data: Detail,
}

定义回调闭包函数get_data,这里针对 JsValue 数据转 rust 数据结构就需要序列化库serde,之前已经安装过了,想要把 JsValue 转换为serde还需要转换工具gloo_utils::format::JsValueSerdeExt,它提供了into_serde/from_serde用于互相转换

let get_data = Closure::wrap(Box::new(move |value: JsValue| {let data = value.into_serde::<DetailResponse>().unwrap();if data.success {data_set.set(data.data);};
}) as Box<dyn FnMut(JsValue)>);

将 JsValue 转换为 rust 数据结构后,就可以取响应数据进行页面渲染了。

这样对比还是使用前一种二次封装的库更好,写法更优雅;更容易理解。毕竟已经习惯使用async..await

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue17:v-bind对css样式的控制增强

代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><styl…

『USB3.0Cypress』FPGA开发(3)GPIF II短包零包时序分析

文章目录 1.时序参数2.FX3_PCLK3.短包和零包3.1短包时序3.2零包ZLP时序 4.传送门 1.时序参数 AN65974文档中明确了操作GPIF II接口时的时序参数&#xff0c;上一篇文章中给出了读写时序图&#xff0c;本篇第二节给出ZLP写周期时序&#xff0c;这里说明相关的时序参数。应该注意…

用户态下屏蔽全局消息钩子 —— ClientLoadLibrary 指针覆盖

目录 前言 一、研究 SetWindowsHookEx 的机制 二、概念验证 三、运行效果分析 四、总结与展望 参考文献 原文出处链接&#xff1a;[https://blog.csdn.net/qq_59075481/article/details/139206017] 前言 SetWindowsHookEx 函数帮助其他人员注入模块到我们的进程&#x…

【代码随想录训练营】【Day 27 and 28】【回溯1-2】| Leetcode 77, 216, 17

【代码随想录训练营】【Day 27 and 28】【回溯1-2】| Leetcode 77, 216, 17 需强化知识点 组合问题&#xff1a;感受遍历的横向和纵向 题目 77. 组合 注意path要深拷贝 class Solution:def combine(self, n: int, k: int) -> List[List[int]]:result []def backtrac…

Kubernetes(k8s) v1.30.1 本地集群部署 安装metallb 支持LoadBalancer 生产环境 推荐 BGP模式部署

1 metallb 安装参考:Kubernetes(k8s) v1.30.1 本地集群部署 默认不支持LoadBalancer metallb来解决-CSDN博客 2 删除 Layer 2 模式 配置 kubectl delete -f IPAddressPool.yaml kubectl delete -f L2Advertisement.yaml kubectl delete -f discuz-srv.yaml 3 配置 k8s Metal…

nacos-opera(k8s)安装问题解决

整理一些关于k8s部署nacos出现的一些恶心的问题 网上说其他说的更改数据库连接都未解决。 在用nacos-opera想安装高可用nacos时连接mysql数据库报错: 报错具体项: No DataSource set 具体就是说没找到数据源。 第一个 检查一下nacos连接数据库配置 : 第二个 检查一下数据库…

[笔试训练](三十三)097:跳台台阶扩展问题098:包含不超过两种字符的最长子串099:字符串的排列

目录 097:跳台台阶扩展问题 098:包含不超过两种字符的最长子串 099:字符串的排列 097:跳台台阶扩展问题 题目链接:跳台阶扩展问题_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 规律题: 1.跳上n级台阶的跳法等于前面1~(n-1)级台阶跳法的总和1。 2.跳…

一、机器学习概述

1.课程目的 学习机器学习算法、提高算法性能的技巧 2.算法分类 有监督学习supervised learning、无监督学习unsupervised learning (1).有监督学习 在这种学习方式中&#xff0c;算法需要一个带有标签的训练数据集&#xff0c;这些标签通常是每个样本的真实输出或类别。 在有…

NDIS小端口驱动(九)

PCIe设备难免会遇到一些重置设备的请求&#xff0c;例如重置总线的时候&#xff0c;但是由于NIC网卡的多样性&#xff0c;重置设备确实也有许多要注意的地方&#xff0c;另外还有一些包含WDM的NDIS驱动 微型端口驱动程序硬件重置 微型端口驱动程序必须向 NdisMRegisterMinipo…

C++技能进阶指南——多态语法剖析

前言&#xff1a;多态是面向对象的三大特性之一。顾名思义&#xff0c; 多态就是多种状态。 那么是什么的多种状态呢&#xff1f; 这里的可能有很多。比如我们去买火车票&#xff0c; 有普通票&#xff0c; 学生票&#xff1b; 又比如我们去旅游&#xff0c; 有儿童票&#xff…

视觉与数据的和谐:数字孪生技术在UI设计中的艺术

视觉与数据的和谐&#xff1a;数字孪生技术在UI设计中的艺术 引言 在UI设计的世界里&#xff0c;视觉艺术与数据科学似乎相隔甚远&#xff0c;然而随着数字孪生技术的出现&#xff0c;这两者之间的界限变得模糊。数字孪生技术不仅是一种技术革新&#xff0c;更是一种艺术形式…

LabviewCarla仿真平台搭建一:平台设计及仿真视频可视化实现

文章目录 背景一、平台设计二、视频显示模块实现1、视频模块实现框架2、python-camera数据生成3、labview-camera数据可视化 三、效果展示 背景 在使用carla的时候&#xff0c;有平台的话可以提高效率&#xff0c;因此想结合labview和carla设计一个仿真平台-labcar。其实carla…

【DASBOOK】Mark loves cat

文章目录 一、工具下载二、Mark loves cat解题感悟 一、工具下载 克隆dirsearch仓库&#xff1a; git clone https://github.com/maurosoria/dirsearch.git下载 githack工具 git clone https://github.com/lijiejie/GitHack.git二、Mark loves cat 用dirsearch扫描目录&…

talib 安装

这里写自定义目录标题 talib 安装出错 talib 安装出错 https://github.com/cgohlke/talib-build/releases 这里找到轮子 直接装。

DatePicker日期选择框(antd-design组件库)简单使用

1.DatePicker日期选择框 输入或选择日期的控件。 2.何时使用 当用户需要输入一个日期&#xff0c;可以点击标准输入框&#xff0c;弹出日期面板进行选择。 组件代码来自&#xff1a; 日期选择框 DatePicker - Ant Design 3.本地验证前的准备 参考文章【react项目antd组件-demo:…

简单3步,ERP、OA、CRM等客户端,安全远程访问服务端

如今&#xff0c;企业员工出差远程办公和分支机构的协同工作变得越来越普遍。然而&#xff0c;如何确保在不同地点的员工都能安全、便捷地访问公司内网的C/S&#xff08;Client/Server&#xff09;架构办公系统&#xff0c;是一个亟待解决的问题。 贝锐花生壳内网穿透服务提供…

基于JAVA GUI体育馆管理系统的会员功能

Java GUI即Java图形用户界面&#xff0c;是一种使用图形化元素&#xff08;如窗口、按钮、文本框等&#xff09;来构建用户界面的技术。它基于Java的Swing框架&#xff0c;可以用于创建各种复杂的用户界面&#xff0c;包括窗口、对话框、菜单、按钮、文本框、复选框、下拉列表等…

SQL学习小记(一)

SQL学习小记&#xff08;一&#xff09; 1. 存储过程&存储函数1.1. 存储过程1.2. 存储函数 2. DEFINER3. INSERT INTO&#xff08;插入新记录&#xff09;4. REPLACE()…AS…5. SUM()函数6. CASE WHEN7. STR_TO_DATE日期时间处理函数8. SUBSTRING函数9. dateFormat函数10. …

神奇动物在哪里?斯洛文尼亚旅游之野生动物寻踪

不仅拥有优美动人的自然风光&#xff0c;斯洛文尼亚还以其丰富的生物多样性而闻名。得益于国家对大自然开展的保护工作&#xff0c;斯洛文尼亚超过三分之一的国土面积都被规划为保护区&#xff0c;拥有约1.5万种动物和6000种植物&#xff0c;其中不乏众多特有、稀有和濒危动植物…

DT浏览器有一些特点和优势,可能是人们选择使用的原因

DT浏览器有一些特点和优势&#xff0c;可能是人们选择使用的原因&#xff1a; - 好评如潮&#xff1a;DT浏览器在网络上获得了众多用户的好评&#xff0c;口碑良好。 - 使用微软搜索引擎技术&#xff1a;DT浏览器采用了微软的搜索引擎技术&#xff0c;在搜索内容上提供了国内…