tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)
项目目标
主要的目的是学习tauri。
流程
1、搭建项目
2、简单的在项目使用leaflet
3、打包
准备项目
环境准备
废话不多说,直接开始
需要有准备能运行Rust的环境和Node,对于Rust可以参考下面这位大佬的文章,Node不必细说。
Rust 和 Cargo 安装指南-CSDN博客
https://blog.csdn.net/qq_44154915/article/details/139365116
建立项目
进入tauri2
官网
https://tauri.app/start/

笔者将使用pnpm建立项目,项目名称为ttvvl
如下

使用VSCode或者WebStorm打开项目
安装依赖
pnpm install

运行
pnpm tauri dev
然后就在compiling

这笔者感到疑惑,可能是第一次运行,搞了许久

运行结果

没有问题。
看一下文件夹的属性

6个G,有点大。
如果以
pnpm run dev
点击按钮,会出现错误


观察上面的代码,我们可以发现,这个是invoke好像是个方法。第一个参数是个字符串greet
第二个参数是个对象,属性是name。
同时找到src-tauri/src/lib.rs中的代码,如下

fn 是Rust的关键字,相当与定义了一个函数,函数名叫greet,参数name
format!是个格式化字符串的函数
其他不是很懂,但我们可以把这段代码给deepseek,问一问
解答如下

从这里我们可以得到关键信息——被这个宏标记的函数能被前端调用。
因为Rust函数的函数名叫greet,而invoke的第一个参数是字符串greet,我们可以很容易猜测
二者必然有关系,我们可以检验一下
修改函数名为greets,运行
发现报错了

我们可以看到下面还有一个函数
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {tauri::Builder::default().plugin(tauri_plugin_opener::init()).invoke_handler(tauri::generate_handler![greet]).run(tauri::generate_context!()).expect("error while running tauri application");
}
可以问一问deepseek

原来下面那个函数是入口函数,invoke_handler是注册器,因此,把中括号里面的greet改成greets
应该可以运行了,(每次都要编译,有点慢)。
如果点击按钮,下面应该不会出现字

事实确实如此。
那么推断很有可能是正确的,那么我们把字符串greet改成greets,就可以出现字了

事实确实如此,笔者明白了。
再次观察,Rust函数greets返回值是String,

而返回格式化后的字符串,对于App.vue中的TypeScript代码,打印出greetMsg.value的值

在运行后的的tauri中,可以打开开发者工具,打印结果如下。

既然如此,感觉明白了,invoke作为Rust与TypeScript交互的关键函数。
安装leaflet有关依赖
pnpm install leaflet leaflet-geoman-free
因为使用typescipt,还需要安装类型定义包
pnpm install --save @types/leaflet.pm @types/leaflet
显示地图
这也是很麻烦的事情。
安装vue-router
pnpm install vue-router
新建一些目录和文件,如下

在router目录的index.ts中
import {createRouter, createWebHashHistory, Router, RouteRecordRaw, RouterOptions} from "vue-router";
const routes:RouteRecordRaw[]=[{path:"/",redirect:"/map",children:[{path:"map",component:()=>import("../views/Map.vue")}]}
]
const options:RouterOptions={history:createWebHashHistory(),routes
}
const router:Router=createRouter(options)
export default router
暂时先这么写
main.ts的内容
import { createApp } from "vue";
import App from "@/App.vue";
import router from "@/router";
import * as L from "leaflet";
import "leaflet/dist/leaflet.css";
import "@geoman-io/leaflet-geoman-free";
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";
const app=createApp(App);
app.use(router);
app.config.globalProperties.$L = L;app.mount("#app");
将L作为Vue的全局属性,并且引入插件leaflet-geoman-free及相关样式
关于@符号,不多解释。
在constant/data.ts中
export const MAP_URL= "https://tile-a.openstreetmap.fr/hot/{z}/{x}/{y}.png";
这个是地图瓦片的URL。通过它就能访问地图。
在utils/Map.ts中
import {MAP_URL} from "@/constant/data.ts";
import L from 'leaflet';
const GetMap = (control: any=null, //控件language:any = 'zh' //语言
): L.Map => {const options: L.MapOptions = {center: [30.6667, 104.0667], //中心点minZoom: 0,maxZoom: 30,zoom: 10,zoomControl: true,doubleClickZoom: true,attributionControl: true,dragging: true,boxZoom: true,scrollWheelZoom: true,zoomSnap: 0.5,};const map = L.map('map', options);L.tileLayer(MAP_URL, {attribution: '',}).addTo(map);map.pm.addControls(control); map.pm.setLang(language);return map;
};
export default GetMap;
如果不写
import L from 'leaflet'
Vue: 'L' refers to a UMD global, but the current file is a module. Consider adding an import instead.
在views/Map.vue中
<script setup lang="ts">
import GetMap from "@/utils/Map.ts";
import { onMounted } from 'vue';
let map: any = null
onMounted(() => {map = GetMap();map.on('click', (e: any) => {console.log(e);});
})
</script><template>
<div id="map"></div>
</template><style scoped>
#map {height: 100vh;width: 200vh;
}
</style>
运行,结果如下

地图定位到成都市。
显示控件
可以查看leaflet-geoman-free文档
Introduction | Documentation for Leaflet-Geoman
https://geoman.io/docs/leaflet在constant/data.ts中
export const MAP_CONTROL={
}
可以写一些设置,但有默认选项,就不改了。
在views/Map.vue中导入MAP_CONTROL,作为GetMap的参数
map = GetMap(MAP_CONTROL);
,使用控件,运行结果如下

搞点小操作1——计算两点之间的距离
操作过程
点两个点,通过invoke函数发送经纬度到rust函数,返回结果
计算距离函数
使用Haversine 公式
haversine公式计算两经纬度点距离-CSDN博客
https://blog.csdn.net/spatial_coder/article/details/116605509使用Rust实现,src-tauri/src/lib.rs的代码如下
const EARTH_RADIUS_KM: f64 = 6371.0; // 地球半径,单位:公里
#[tauri::command]
// 计算两个经纬度点之间的距离(单位:公里)
fn haversine_distance(start:[f64;2],end:[f64;2]) -> f64 {// 将经纬度从度数转换为弧度let lat1_rad = start[0].to_radians();let lon1_rad = start[1].to_radians();let lat2_rad = end[0].to_radians();let lon2_rad = end[1].to_radians();// 经纬度差值let dlat = lat2_rad - lat1_rad;let dlon = lon2_rad - lon1_rad;// Haversine 公式let a = (dlat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (dlon / 2.0).sin().powi(2);let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());// 计算距离EARTH_RADIUS_KM * c
}#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {tauri::Builder::default().plugin(tauri_plugin_opener::init()).invoke_handler(tauri::generate_handler![haversine_distance]).run(tauri::generate_context!()).expect("error while running tauri application");
}
以公里作为单位
对两个点marker的思考
点了两个点marker,我们通过leaflet和有关插件的某些事件可以获得这两个点的属性,
我们可以修改这些属性。
那应该然后修改?
可以尝试使用tauri的多窗口。
marker位于父窗口,发送信号到子窗口,在子窗口进行修改,返回父窗口。
父子窗口以及发送信号

关于窗口的配置
src-tauri/capabilities/default.json
{"$schema": "../gen/schemas/desktop-schema.json","identifier": "default","description": "Capability for the main window","windows": ["main","marker-name"],"permissions": ["core:default","opener:default","core:window:allow-hide","core:window:allow-show"]
}
在子窗口添加一个路由
src/router/index.ts
{path:"/name",component:()=>import("../views/name.vue")}
src-tauri/tauri.conf.json
"windows": [{"title": "ttvvl","label": "main","width": 800,"height": 600},{"title": "点位窗口","width": 400,"height": 300,"label": "marker-name","resizable": true,"parent": "main","visible": false,"url":"#/name","decorations": false,"center": true}],
参数的具体含义,可以参考官网或者deekssek
{"windows": [{"label": "main", // 窗口的唯一标识符"title": "My Tauri App", // 窗口标题"width": 800, // 窗口宽度"height": 600, // 窗口高度"x": null, // 窗口初始水平位置(null 表示由系统决定)"y": null, // 窗口初始垂直位置(null 表示由系统决定)"minWidth": null, // 窗口最小宽度"minHeight": null, // 窗口最小高度"maxWidth": null, // 窗口最大宽度"maxHeight": null, // 窗口最大高度"resizable": true, // 是否允许调整窗口大小"fullscreen": false, // 是否全屏"focus": true, // 窗口是否在创建时获得焦点"visible": true, // 窗口是否可见"decorations": true, // 是否显示窗口装饰(标题栏、边框等)"alwaysOnTop": false, // 窗口是否始终置顶"maximized": false, // 窗口是否最大化"transparent": false, // 窗口背景是否透明"center": true, // 窗口是否居中"theme": "system", // 窗口主题("system"、"light"、"dark")"url": "index.html", // 窗口加载的页面路径"fileDropEnabled": true, // 是否启用文件拖放功能"skipTaskbar": false, // 是否在任务栏中显示窗口"shadow": true, // 是否显示窗口阴影"acceptFirstMouse": false, // 是否接受首次鼠标点击事件"tabbingIdentifier": null, // macOS 标签页标识符"titleBarStyle": "visible", // macOS 标题栏样式("visible"、"transparent"、"overlay")"hiddenTitle": false // 是否隐藏标题栏标题}]
}
父窗口
src/views/Map.vue
<script setup lang="ts">
//@ts-nocheck
import GetMap from "@/utils/Map.ts";
import {MAP_CONTROL} from "@/constant/data.ts";
import {invoke} from "@tauri-apps/api/core";
import {emit,listen} from "@tauri-apps/api/event";
import {onMounted, ref} from 'vue';
import WindowManager from '@/utils/window';const windowManager = new WindowManager();
let startName=ref<String>('起点');
let endName=ref<String>('终点');
let markerWindow,mainWindow,markerIndex=null;
let map: any = null
let markerList: L.Marker[] = [];
let distance=ref<number>(0);listen('message-to-main-window', (event)=>{ //监听消息let payload:any=event.payloadif(payload.close){windowManager.closeWindow('marker-name')mainWindow.show()}if(markerIndex===null){return}if(markerIndex===0){startName.value='起点'+payload.tooltipContent}else{endName.value='终点'+payload.tooltipContent}markerList[markerIndex].bindTooltip(payload.tooltipContent)
});
async function sendMsg(tooltipContent:String,nowClickMarkerId:Number) { //发送消息markerWindow=await windowManager.getWindowByLabel('marker-name') //子窗口mainWindow=await windowManager.getWindowByLabel('main') //父窗口if(markerWindow) {mainWindow.hide()markerWindow.show();markerIndex=markerList.findIndex((marker:L.Marker)=>marker._leaflet_id===nowClickMarkerId)emit('message-to-second-window', { //发送消息 参数lat: markerList[0].getLatLng().lat,lng: markerList[0].getLatLng().lng,markerIndex:markerIndex,tooltipContent:tooltipContent})}
}async function get_distance(){let start=markerList[0].getLatLng(); //开始点let end=markerList[1].getLatLng(); //终点let startArray=[start.lat,start.lng];let endArray=[end.lat,end.lng];distance.value = await invoke('haversine_distance', { //发送给rust tauristart: startArray,end: endArray});
}
onMounted(() => {// openWindow()map = GetMap(MAP_CONTROL);map.on('pm:create', (e: any) => { // map create事件let marker:L.Marker = e.marker;marker.on('click',(e)=>{ // marker的点击事件let nowClickMarkerId=e.target._leaflet_id // 获取marker的idsendMsg(e.target.getTooltip(),nowClickMarkerId) // 发送消息})markerList.push(marker);let length = markerList.length;if(length==2){ // 起始点 和终点get_distance() //获取距离}else if(length>2){markerList[0].remove(); //移除第一个点markerList.shift();get_distance() // 获取距离}})})
</script><template>
<div id="map"><div id="distance-text" v-if="markerList.length>=2">从{{startName}}到{{endName}}的距离为</div><div><button type="button" id="show" >{{distance}}</button></div><div id="unit">公里</div></div>
</template><style scoped>
#map {margin: 0 auto;width: 100%;height: 900px;
}
#show {max-width: 200px;max-height: 50px;padding: 10px;background-color: blue;color: white;text-align: center;position: absolute;top: 100px;right: 10px;z-index: 999;border-radius: 20px;
}
#distance-text {max-width: 200px;max-height: 50px;padding: 10px;background-color: #af8433;color: white;text-align: center;position: absolute;top: 10px;right: 10px;z-index: 999;border-radius: 20px;
}
#unit {max-width: 200px;max-height: 50px;padding: 10px;background-color: #af8433;color: white;text-align: center;position: absolute;top: 150px;right: 10px;z-index: 999;border-radius: 20px;
}</style>
子窗口
src/views/Name.vue
<script setup>
import {listen,emit} from "@tauri-apps/api/event";
import {onMounted, ref} from "vue";
let lat = ref(0);
let lng = ref(0);
let markerIndex = ref(0); // 0 替换成起点 1 替换成终点
let inputValue = ref('');
let oldTooltip=nullconst handleClicked = () => {emit('message-to-main-window', {tooltipContent:inputValue.value,close: true,});
}
const handleCancel = () => {emit('message-to-main-window', {tooltipContent:oldTooltip,close: true,});
}
onMounted(() => {listen('message-to-second-window', (event) => {let payload = event.payload;lat.value = payload.lat;lng.value = payload.lng;markerIndex.value = payload.markerIndex;inputValue.value=payload.tooltipContent;oldTooltip=payload.tooltipContent});}
)</script>
<template><div data-tauri-drag-region class="titlebar" ><h1>{{markerIndex===0?'起点':'终点'}}</h1><div v-if="lat>0">纬度:{{lat }}</div><div v-if="lng>0">经度:{{lng }}</div><div>名称<br/><input v-model="inputValue"></input></div><br/><button type="button" @click="handleClicked" class="sure">确定</button><button type="button" @click="handleCancel" class="cancel">取消</button></div>
</template>
<style >
.sure{padding: 10px ;margin-right: 10vw;background-color: #5174ff;
}
.cancel{padding: 10px ;margin-right: 10vw;background-color: #ffb43e;}</style>
窗口管理
src/utils/window.ts
import { getAllWebviewWindows } from '@tauri-apps/api/webviewWindow';export class WindowManager {async getWindowByLabel(label: string) {const windows = await getAllWebviewWindows();return windows.find((win) => win.label === label);}async closeWindow(label: string) {const window = await this.getWindowByLabel(label);if (window) {window.hide();}}
}export default WindowManager;
运行结果



运行结果倒是没问题,代码写得不行,算了。不管那些。
打包
本地打包
打包成exe命令
pnpm tauri build
因为笔者不是第一次打包,如果是第一次打包,需要下载一些东西,笔者就不展示了,可参考下面这位大佬的过程。
从零开始的 Tauri 开发 & 打包成 exe 【Windows 平台】_tauri 打包-CSDN博客
https://blog.csdn.net/u010263423/article/details/136006546对于leaflet打包,会出现一个bug,解决过程可看这篇文章
解决Vue+Vite打包后Leaflet的marker图标不显示的问题_leaflet l.marker 没有-CSDN博客
https://blog.csdn.net/qq_63401240/article/details/139972362?spm=1001.2014.3001.5502总之。打包结果

打包运行结果
可以。

上传到github并打包
在github上通过工作流打包
工作流代码参考下面这位大佬
tauri使用github的action自动发布release,让别人也可以看到下载链接_tauri github action-CSDN博客
https://blog.csdn.net/weixin_44786530/article/details/140904091在大佬的基础上做出小的修改

修改了工件的版本,其他改动不大
运行结果如下

发现ubuntu出错了。笔者没有深究。
笔者再次打包,只选择了window系统,结果如下

项目地址
qe-present/ttvvl
https://github.com/qe-present/ttvvl
总结
简单地使用了tauri,使用了信号通信,多窗口,打包。
相关文章:
tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)
项目目标 主要的目的是学习tauri。 流程 1、搭建项目 2、简单的在项目使用leaflet 3、打包 准备项目 环境准备 废话不多说,直接开始 需要有准备能运行Rust的环境和Node,对于Rust可以参考下面这位大佬的文章,Node不必细说。 Rust 和…...
本地部署阿里万象2.1文生视频模型(Wan2.1-T2V)完全指南
在生成式AI技术爆发式发展的今天,阿里云开源的万象2.1(Wan2.1)视频生成模型,为创作者提供了从文字/图像到高清视频的一站式解决方案。本文针对消费级显卡用户,以RTX 4060 Ti 16G为例,详解本地部署全流程与性能调优方案,涵盖环境配置、多模型选择策略、显存优化技巧及实战…...
# [Linux] [Anaconda]解决在 WSL Ubuntu 中安装 Anaconda 报错问题
在 Windows 10 中安装了 WSL(Windows Subsystem for Linux)并使用 Ubuntu 后,你可能会下载 Anaconda 的 Linux 版本进行安装。但在安装过程中,可能会遇到 tar (child): bzip2: Cannot exec: No such file or directory 这样的错误…...
ES怎么查询大于10000条数据
在Elasticsearch(ES)中,默认情况下,查询结果的最大返回条数是10,000条。如果你需要查询超过10,000条数据,可以通过以下几种方式来实现: 1. 使用 scroll API scroll API 适用于需要处理大量数据的场景&…...
【Vue CLI脚手架开发】——3.组件交互props配置
文章目录 前言一、props数据接收方式二、代码实现1. 父组件2.子组件 三、分析 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习…...
FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解
FPGA之Usb数据传输 Usb 通信 你也许会有疑问,明明有这么多通信方式和数据传输(SPI、I2C、UART、以太网)为什么偏偏使用USB呢? 原因有很多,如下: 1. 高速数据传输能力 高带宽:USB接口提供了较高的数据传…...
【Office-Word】如何自动生成中英文目录
1.目录介绍 Word这个自动生成目录非常强大,涉及的功能很琐碎,想要完美的生成目录不仅仅是只会目录这么简单,前后涉及到的大纲级别、目标样式和域代码等操作是比较头疼的。 下面就一步一步开始介绍 2.多级标题级别编号设置 目录想要设置好…...
Oracle删除重复数据保留其中一条
Oracle删除重复数据保留其中一条 在Oracle数据库中,要删除重复数据并保留其中一条记录,可以使用多种方法。这里介绍两种常见的方法:使用ROWID或使用ROW_NUMBER()窗口函数。 方法1:使用ROWID ROWID是Oracle中用来唯一标识表中每…...
CentOS 7 安装Nginx-1.26.3
无论安装啥工具、首先认准了就是官网。Nginx Nginx官网下载安装包 Windows下载: http://nginx.org/download/nginx-1.26.3.zipLinxu下载 wget http://nginx.org/download/nginx-1.26.3.tar.gzLinux安装Nginx-1.26.3 安装之前先安装Nginx依赖包、自行选择 yum -y i…...
家政预约小程序用例图分析
在和客户进行需求沟通的时候,除了使用常规的问答的形式,我还使用图形化工具更深入的沟通。比如借助UML的用例图来开展系统分析,并且按照角色详细拆解了家政预约小程序的各个用例。在分析阶段思考的越多,沟通的越多,在系…...
112页精品PPT | DeepSeek行业应用实践报告
这份文件是一份关于DeepSeek行业应用实践的报告,以PPT形式呈现,共112页,详细介绍了DeepSeek及其核心产品DeepSeek-R1的技术特点、市场表现、应用路径以及在多领域的实践案例。报告展示了DeepSeek在市场上的快速崛起,包括其日活用户…...
计算机毕业设计SpringBoot+Vue.js航空机票预定系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
C语言学习笔记-初阶(27)操作符详解1:位操作
1. 操作符的分类 上述的操作符,我们已经学过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符,今天继续介绍⼀部分,操作符中有一些操作符和二进制有关系,我们先铺垫一下二进制的和进制转换的知识。 2. 二进制、…...
网络安全需要学多久才能入门?
网络安全是一个复杂且不断发展的领域,想要入行该领域,我们需要付出足够多的时间和精力好好学习相关知识,才可以获得一份不错的工作,那么网络安全需要学多久才能入门?我们通过这篇文章来了解一下。 学习网络安全的入门时间因个人的…...
20250304学习记录
第一部分,先来了解一下各种论文期刊吧,毕竟也是这把岁数了,还什么都不懂呢 国际期刊: EI收集的主要有两种, JA:EI源刊 CA:EI会议 CPCI也叫 ISTP 常说的SCI分区是指,JCR的一区、…...
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架
【星云 Orbit • STM32F4】08. 用判断数据头来接收据的串口通用程序框架 1. 引言 本教程旨在帮助嵌入式开发小白从零开始,学习如何在STM32F407微控制器上实现一个基于串口的数据接收程序。该程序能够通过判断数据头来接收一串数据,并将其存储到缓冲区中…...
文件上传复现
文件上传漏洞的概念 在现代互联网的web应用程序中,上传文件是一种常见的功能,因为它有助于提高业务效率,比如社交 网站中,允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多, web应 用受到…...
Redis——缓存穿透、击穿、雪崩
缓存穿透 什么是缓存穿透 缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库…...
HMC7043和HMC7044芯片配置使用
一,HMC7043芯片 MC7043独特的特性是对14个通道分别进行独立灵活的相位管理。所有14个通道均支持频率和相位调整。这些输出还可针对50 Ω或100 Ω内部和外部端接选项进行编程。HMC7043器件具有RF SYNC功能,支持确定性同步多个HMC7043器件,即确保所有时钟输出从同一时钟沿开始…...
STM32程序的加密与破解以及烧录方法
STM32程序的加密与破解,以及烧录方法。 盗取他人的PCB和烧录文件,可以节省大大开发成本,何乐而不为呢。因此,就滋生了一些协助他人盗版的公司。为了防止被盗版和复制,单片机工程师也是煞费苦心,对硬件和软…...
Redis和MySQL的实时数据同步方案
针对 Redis 和 MySQL 的实时数据同步,需根据业务场景选择不同的技术方案,核心目标是保障数据一致性、降低延迟、提升系统可靠性。以下是几种典型方案及其适用场景: 方案一:基于 MySQL Binlog 的异步同步 原理 监听 MySQL 的 Bin…...
VSCode知名主题带毒 安装量900万次
目前微软已经从 Visual Studio Marketplace 中删除非常流行的主题扩展 Material Theme Free 和 Material Theme Icons,微软称这些主题扩展包含恶意代码。 统计显示这些扩展程序的安装总次数近 900 万次,在微软实施删除后现在已安装这些扩展的开发者也会…...
JavaScript 进阶A(作用域、闭包、变量和函数提升、函数相关只是、数组解构、对象解构、构造函数
1.作用域 作用域主要分为:局部作用域和全局作用域。 局部作用域又分为:函数作用域和块作用域 函数作用域:在函数中定义的变量只能在函数内部使用,外部无法访问块作用域:被大括号{}包起来的代码块,在这个…...
mybatis映射文件相关的知识点总结
mybatis映射文件相关的知识点总结 mybatis官网地址 英文版:https://mybatis.org/mybatis-3/index.html 中文版:https://mybatis.p2hp.com/ 搭建环境 /* SQLyog Ultimate v10.00 Beta1 MySQL - 8.0.30 : Database - mybatis-label *****************…...
【UCB CS 61B SP24】Lecture 21: Data Structures 5: Priority Queues and Heaps 学习笔记
本文介绍了优先队列与堆,分析了最小堆的插入与删除过程,并用 Java 实现了一个通用类型的最小堆。 1. 优先队列 1.1 介绍 优先队列是一种抽象数据类型,其元素按照优先级顺序被处理。不同于普通队列的先进先出(FIFO)&…...
【JAVA】ThreadPoolTaskExecutor 线程池学习、后端异步、高并发处理
ThreadPoolTaskExecutor 是 Spring 框架提供的一个线程池实现类,基于 Java 原生的 ThreadPoolExecutor 进行了封装和扩展,支持更灵活的配置,并与 Spring 的依赖注入、生命周期管理等功能无缝集成。它常用于异步任务处理、定时任务调度和高并发…...
C#:LINQ学习笔记01:LINQ基础概念
一、LINQ 架构体系 1. LINQ 的核心思想 统一查询模型:对对象、XML、数据库等不同数据源使用一致的语法。强类型检查:编译时类型安全,减少运行时错误。 2. 核心组件 技术数据源典型场景LINQ to Objects内存集合 (IEnumerable)过滤/排序集合…...
爬虫系列之发送请求与响应《一》
一、请求组成 1.1 请求方式:GET和POST请求 GET:从服务器获取,请求参数直接附在URL之后,便于查看和分享,常用于获取数据和查询操作 POST:用于向服务器提交数据,其参数不会显示在URL中,而是包含在…...
【零基础到精通Java合集】第十集:List集合框架
课程标题:List集合框架(15分钟) 目标:掌握List接口核心实现类(ArrayList/LinkedList)的使用与场景选择,熟练操作有序集合 0-1分钟:List概念引入 以“购物清单”类比List特性:元素有序(添加顺序)、可重复、支持索引访问。说明List是Java集合框架中最常用的数据结构…...
小米手机如何录制屏幕?手机、电脑屏幕录制方法分享
大家最近有没有遇到想记录手机屏幕操作的情况? 比如精彩的游戏瞬间、有趣的视频教程,或者需要录制屏幕来制作演示材料。小米手机在这方面可是个好帮手,今天就来给你好好唠唠,小米手机如何录制屏幕,以及后续如何处理这…...
