当前位置: 首页 > news >正文

electron + vue3 + vite 渲染进程与渲染进程之间的消息端口通信

渲染进程与渲染进程之间的通信有两种:

  • 通过主进程进行消息转发(通过组合主进程与渲染进程之间的单向、双向通信可以实现,可以自己动手尝试,该篇不讲解)
  • 通过消息端口进行直接通信

该篇主要用示例讲解下单项目内多个窗口的渲染进程之间相互通信的流程(也就是通过消息端口进行通知)
初始版本项目结构可参考项目:https://github.com/ylpxzx/electron-forge-project/tree/init_project

请添加图片描述

渲染进程与渲染进程之间的消息端口通信

实现整项目示例:https://github.com/ylpxzx/electron-forge-project/tree/message_port

MessageChannelMain方法

示例:

const { port1, port2 } = new MessageChannelMain()

MessageChannelMain方法用于创建一个消息通道,该通道包含两个端口 port1 和 port2。这两个端口可以用于在不同的上下文(如主进程和渲染进程)之间传递消息

通信逻辑

通过模拟主窗口(Main)与配置窗口(Settings)之间的通信,来了解下如何通过消息端口实现渲染进程之间的通信

新增Settings窗口预加载文件
  • 创建src/settingPreload.js文件

    该预加载文件用于暴露可用的Electron API给Settings窗口页面调用

    该文件创建完成后,需要在forge.config.js文件的plugins字段进行配置

    {name: '@electron-forge/plugin-vite',config: {build: [{entry: 'src/main.js',config: 'vite.main.config.mjs',target: 'main',},{entry: 'src/preload.js',config: 'vite.preload.config.mjs',target: 'preload',},{ // 加入该配置entry: 'src/settingPreload.js',config: 'vite.preload.config.mjs',target: 'preload',},],renderer: [{name: 'main_window',config: 'vite.renderer.config.mjs',},],},},
    
主进程下发消息端口给窗口
  • src/main.js文件
    请添加图片描述
    端口下发逻辑:

    • 在主进程main.js文件中,调用消息体MessageChannelMain方法,得到两个可互相通信的端口port1、port2

    • 将消息体端口port1、port2分别下发给准备就绪后的main窗口和settings窗口(即下发给两个渲染进程)

      // 将port1端口下发给main窗口
      mainWindow.once('ready-to-show', () => {mainWindow.webContents.postMessage('main-port', null, [port1])
      })// 将port2端口下发给settings窗口
      settingsWindow.once('ready-to-show', () => {settingsWindow.webContents.postMessage('settings-port', null, [port2])
      })
      

    完整代码如下:

    import { app, BrowserWindow, MessageChannelMain } from 'electron';
    import path from 'node:path';
    import started from 'electron-squirrel-startup';// Avoid Warning:Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security
    process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";if (started) {app.quit();
    }// crerate message channel
    const { port1, port2 } = new MessageChannelMain()// 创建Main窗口
    const createWindow = () => {const mainWindow = new BrowserWindow({width: 800,height: 600,webPreferences: {preload: path.join(__dirname, 'preload.js'),},});if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);} else {mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));}// webContents准备就绪后,使用postMessage向webContents发送端口main-portmainWindow.once('ready-to-show', () => {mainWindow.webContents.postMessage('main-port', null, [port1])})mainWindow.webContents.openDevTools();
    };// 创建Settings窗口
    const createSettingsWindow = () => {const settingsWindow = new BrowserWindow({width: 800,height: 600,webPreferences: {nodeintegration: true,preload: path.join(__dirname, 'settingPreload.js'),},});if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {const settingUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/settings`;settingsWindow.loadURL(settingUrl);} else {mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));}// webContents准备就绪后,使用postMessage向webContents发送端口settings-portsettingsWindow.once('ready-to-show', () => {settingsWindow.webContents.postMessage('settings-port', null, [port2])})settingsWindow.webContents.openDevTools();
    };app.whenReady().then(() => {// app准备好后,创建两个窗口createWindow();createSettingsWindow();app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) {createWindow();}});
    });app.on('window-all-closed', () => {if (process.platform !== 'darwin') {app.quit();}
    });
    
在预加载文件暴露可调用的Electron API给两个窗口页面
  • src/preload.js

    const { contextBridge, ipcRenderer } = require('electron/renderer')let electronMessage = nullipcRenderer.on('main-port', e => {electronMessage = e.ports[0]
    })contextBridge.exposeInMainWorld('electronAPI', {// 向端口发送消息pushMessageEvent: (message) => electronMessage.postMessage(message),// 监听端口消息onMessagePort: (callback) => {const listener = (e) => {e.ports[0].onmessage = messageEvent => {callback(messageEvent.data)}}ipcRenderer.on('main-port', listener)return () => ipcRenderer.removeListener('main-port', listener)},
    })
    

    暴露给页面调用的API有两个:发送消息API、监听消息API; 都是通过ipcRenderer.on实现。

    • 发送消息API:需在上下文隔离外(contextBridge)先实例化消息示例(electronMessage), 然后通过postMessage方法推送消息给端口。

      如果在contextBridge内直接调用e.ports[0].postMessage(message), 会发送不成功。如下错误示例(没有任何报错):

      pushMessageEvent: (message) => {ipcRenderer.on('main-port', e => {e.ports[0].postMessage(message)})
      },
      

      具体为啥会失败,目前没找到原因。如果有人能答下疑惑,可以在评论区解答下

    • 监听消息API:通过e.ports[0].onmessage方法监听端口,接收到消息时触发对应的回调函数callback

  • src/settingPreload.js 同上

    const { contextBridge, ipcRenderer } = require('electron/renderer')
    let electronMessageTest = nullipcRenderer.on('settings-port', e => {electronMessageTest = e.ports[0]
    })contextBridge.exposeInMainWorld('electronSettingAPI', {pushMessageEvent: (message) => electronMessageTest.postMessage(message),onMessagePort: (callback) => {const listener = (e) => {e.ports[0].onmessage = messageEvent => {callback(messageEvent.data)}}ipcRenderer.on('settings-port', listener)return () => ipcRenderer.removeListener('settings-port', listener)},
    })
    

页面示例

通信逻辑实现后,接下来就用两个窗口页面来验证结果

  • src/vue-project/pages/home/index.vue

    <template><h1>😁 Main Render Process</h1><div style="padding-bottom: 10px;">Receive messages from the Settings window process:<span style="color: #71C25C;">{{messageVal }}</span></div><div style="display: flex; gap: 10px;"><input id="inputID" ref="inputRef" v-model="inputVal" /><button @click="onClick">Send to Settings window</button></div>
    </template><script setup>
    import { ref, onUnmounted, onMounted } from 'vue'
    const inputRef = ref(null)
    const inputVal = ref('')
    const messageVal = ref(null)
    const onClick = () => {// 点击发送消息electronAPI.pushMessageEvent(inputVal.value)
    }
    let removeLister = nullonMounted(() => {// 监听接收settins窗口回传的消息removeLister = electronAPI.onMessagePort(async (value) => {messageVal.value = value})
    })onUnmounted(() => {// 页面移除时,移除监听removeLister()
    })
    </script>
    
  • src/vue-project/pages/settings/index.vue

    <template><h1>😊 Settings Render Process</h1><div style="padding-bottom: 10px;">Received message from the main window process:<span style="color: #71C25C;">{{message}}</span></div><div style="display: flex; gap: 10px;"><input v-model="inputVal" /><button @click="onClick">Send to Main window</button></div>
    </template><script setup>
    import { ref, onUnmounted, onMounted } from 'vue'
    const message = ref('')
    const inputVal = ref('')const onClick = () => {// 点击发送消息electronSettingAPI.pushMessageEvent(inputVal.value)
    }let removeLister = nullonMounted(() => {// 监听端口消息,当接收到消息时,再回传一段话给main窗口removeLister = electronSettingAPI.onMessagePort((value) => {message.value = valueelectronSettingAPI.pushMessageEvent(`👋 Hello main window, I have received your message, message is ${message.value}`)})
    })onUnmounted(() => {// 页面移除时,移除监听removeLister()
    })
    </script>
    
  • src/vue-project/router/index.js

    import { createWebHashHistory, createRouter } from 'vue-router'import HomeView from '@/vue-project/pages/home/index.vue'
    import SettingsView from '@/vue-project/pages/settings/index.vue'const routes = [{ path: '/', component: HomeView },{ path: '/settings', component: SettingsView },
    ]
    const router = createRouter({history: createWebHashHistory(),routes,
    })export default router;
    
  • src/vue-project/App.vue

    <template><h1>🖥️ Hello World!</h1><p>Welcome to your Electron application.</p><p>🎉 Welcome to your Electron <span style="font-weight: 600;">{{ isMain ? 'Main' : 'Settings' }} Window </span>. 🎊</p><divstyle="margin-top: 20px; border: 1px solid grey; padding: 20px; border-radius: 6px; background-color: #23272E; color: #fff"><router-view></router-view></div>
    </template><script setup>
    import { ref } from 'vue'
    const isMain = ref(true)
    if (window.electronAPI) {isMain.value = true
    } else {isMain.value = false
    }
    </script>
    

示例演示:

在这里插入图片描述

解决潜在问题:

问题1:从其他页面切换回来时,消息端口监听失效

  • src/vue-project/pages/empty/index.vue

    新增一个空页面,用于复现从其他页面切换回来后,消息端口监听失效问题

    <template><h1>😁Test whether the message communication is still valid after the page is switched back.</h1>
    </template><script setup>
    </script>
    
  • src/vue-project/router/index.js

    注册empty页面路由

    import { createWebHashHistory, createRouter } from 'vue-router'import HomeView from '@/vue-project/pages/home/index.vue'
    import SettingsView from '@/vue-project/pages/settings/index.vue'
    import EmptyView from '@/vue-project/pages/empty/index.vue'const routes = [{ path: '/', component: HomeView },{ path: '/settings', component: SettingsView },{ path: '/empty', component: EmptyView },
    ]
    const router = createRouter({history: createWebHashHistory(),routes,
    })export default router;
    
  • src/vue-project/App.vue

    <template><h1>🖥️ Hello World!</h1><p>Welcome to your Electron application.</p><p>🎉 Welcome to your Electron <span style="font-weight: 600;">{{ isMain ? 'Main' : 'Settings' }} Window </span>. 🎊</p><nav><div v-if="isMain"><RouterLink to="/">Go to Home</RouterLink></div><div v-if="isMain"><div><RouterLink to="/empty">Go to empty page<span style="font-size: 12px; color: #999; padding-left: 10px;"></span></RouterLink></div></div></nav><divstyle="margin-top: 20px; border: 1px solid grey; padding: 20px; border-radius: 6px; background-color: #23272E; color: #fff"><router-view></router-view></div>
    </template><script setup>
    import { ref } from 'vue'
    const isMain = ref(true)
    if (window.electronAPI) {isMain.value = true
    } else {isMain.value = false
    }
    </script>
    
  • 问题复现

    在这里插入图片描述

  • 解决方法

    vue页面没缓存的情况下,页面会直接被移除,导致之前的监听也丢失。需要给页面路由加上<keep-alive>

    src/vue-project/App.vue

    <!-- 该处得加入keep-alive,否则切换路由时会重新渲染组件,导致消息端口监听器失效 -->
    <router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive>
    </router-view>
    

问题2:项目打包构建npm run make后运行,settings窗口显示报错

请添加图片描述

  • 解决方法:

    报错的原因是在生产环境下,找不到settings窗口的路由导致的,更改下生产环境的页面路由路径就可以了

    src/main.js

    // 其他代码...
    const createSettingsWindow = () => {const settingsWindow = new BrowserWindow({width: 800,height: 600,webPreferences: {nodeintegration: true,preload: path.join(__dirname, 'settingPreload.js'),},});if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {const settingUrl = `${MAIN_WINDOW_VITE_DEV_SERVER_URL}/#/settings`;settingsWindow.loadURL(settingUrl);} else {// 增加此行代码:hash: 'settings', 保证打包好的生产环境下正常跳转settingsWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`), { hash: 'settings' });}settingsWindow.once('ready-to-show', () => {settingsWindow.webContents.postMessage('settings-port', null, [port2])})settingsWindow.webContents.openDevTools();
    };
    // 其他代码...
    

项目结构

请添加图片描述

相关文章:

electron + vue3 + vite 渲染进程与渲染进程之间的消息端口通信

渲染进程与渲染进程之间的通信有两种&#xff1a; 通过主进程进行消息转发&#xff08;通过组合主进程与渲染进程之间的单向、双向通信可以实现&#xff0c;可以自己动手尝试&#xff0c;该篇不讲解&#xff09;通过消息端口进行直接通信 该篇主要用示例讲解下单项目内多个窗口…...

MySQL性能调优实战手册:从慢查询到执行计划全解析

一、调优流程四步走 &#x1f680; 当我们遇到数据库调优问题的时候&#xff0c;该如何思考呢? 这里把思考的流程整理成下面这张图。 整个流程划分成了观察&#xff08;Show status&#xff09;和 行动(Action&#xff09;两个部分。字母S的部分代表观察&#xff08;会使用相…...

作为高数小白,我尝试理解概念:高数 - 导数

作为C#开发者&#xff0c;其实我是一个高数小白&#xff0c;可是我对它极其好奇&#xff0c;非常想了解它里面到底讲了什么内容&#xff0c;貌似这对于我是一个非常艰难的过程&#xff0c;因为完全没有接触过高等数学&#xff0c;可是又太想去了解。所以我计划开始慢慢学习它。…...

在 IntelliJ IDEA(2024) 中创建 JAR 包步骤

下是在 IntelliJ IDEA 中创建 JAR 包的详细的步骤&#xff1a; ​1. 选择File -> Project Structure->Artifacts&#xff0c; (1)点击➕新建&#xff0c;如下图所示&#xff1a; (2)选择JAR->Empty (3)输入jar包名称&#xff0c;确定输出路径 &#xff08;4&#…...

『PostgreSQL』PGSQL备份与还原实操指南

&#x1f4e3;读完这篇文章里你能收获到 了解逻辑备份与物理备份的区别及适用场景&#x1f50d;。掌握全库、指定库、指定表备份还原的命令及参数&#x1f4dd;。学会如何根据业务需求选择合适的备份策略&#x1f4ca;。熟悉常见备份还原问题的排查与解决方法&#x1f527;。 …...

Mentalab Explore 在低密度 EEG 系统中的创新应用

在当今科技飞速发展的时代&#xff0c;脑电图&#xff08;EEG&#xff09;技术已成为神经科学研究、医疗诊断和教育领域广泛使用的工具。Mentalab Explore 作为一款专为灵活性和高效性设计的便携式低密度 EEG 系统&#xff0c;凭借其性能和多样的应用场景&#xff0c;正引领着便…...

Ubuntu 24.04.2 允许 root 登录桌面、 ssh 远程、允许 Ubuntu 客户机与主机拖拽传递文件

允许 root 登录桌面 修改 /etc/pam.d/gdm-autologin , /etc/pam.d/gdm-password 加 # 以注释掉 auth required pam_succeed_if.so user ! root quiet_success 允许 root 通过 ssh 登录 修改 /etc/ssh/sshd_config ... #PermitRootLogin prohibit-password PermitRootLogin …...

ngx_openssl_create_conf

ngx_openssl_create_conf 声明在 src\event\ngx_event_openssl.c static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); 定义在 src\event\ngx_event_openssl.c static void * ngx_openssl_create_conf(ngx_cycle_t *cycle) {ngx_openssl_conf_t *oscf;oscf ngx_…...

数学 二次函数

二次函数 就是计算一个抛物线。 抛物线的基本公式&#xff1a; 重点中的重点就是解决&#xff1a; &#xff08;开口方向&#xff1a; 对称轴&#xff0c;顶点&#xff0c;交点&#xff09; 这里的 y 和 x 就是 这个抛物线的个个点的坐标连成的线。 a 的正负 决定和大小决定…...

系统架构的评估的系统的质量属性

体系结构苹果可以针对一个体系结构&#xff0c;也可以针对一组体系结构。 体系结构评估过程中&#xff0c;评估人员所关注的是系统的质量属性&#xff0c;所有评估方法所普遍关注的质量属性有以下几个&#xff1a;性能、可靠性&#xff08;容错&#xff0c;健壮性&#xff09;…...

Facebook 的历史与发展:从校园网站到全球社交平台

引言 Facebook&#xff0c;这个全球最大的社交网络平台之一&#xff0c;其发展历程充满了创新和变革。从最初的校园网站到如今的全球社交平台&#xff0c;Facebook 不仅改变了人们的沟通方式&#xff0c;也重塑了信息传播和社交互动的模式。 起源&#xff1a;校园内的点子 Fa…...

开源、创新与人才发展:机器人产业的战略布局与稚晖君成功案例解析

目录 引言 一、开源&#xff1a;机器人产业的战略布局 促进技术进步和生态建设 吸引人才和合作伙伴 建立标准和网络效应 降低研发风险与成本 二、稚晖君&#xff1a;华为"天才少年计划"的成功典范 深厚的技术积累与动手能力 强烈的探索和创新意识 持续公开…...

Visual Studio 2022新建c语言项目的详细步骤

步骤1&#xff1a;点击创建新项目 步骤2&#xff1a;到了项目模板 --> 选择“控制台应用” (在window终端运行代码。默认打印"Hello World") --> 点击 “下一步” 步骤3&#xff1a;到了配置新项目模块 --> 输入“项目名称” --> 更改“位置”路径&…...

利用FatJar彻底解决Jar包冲突(三)

利用FatJar彻底解决Jar包冲突 Spring 容器的加载与隔离⽀持注解配置⽂件定位与容器初始化嵌套Spring容器的加载 隔离优化EagleEye traceId不⼀致问题原因解决 Spring 容器的加载与隔离 ⽀持注解 这个⽐较容易&#xff0c;主要是我们之前的应⽤不⽀持⼆⽅包内部的注解&#xf…...

【TMS570LC4357】之工程创建

备注&#xff1a;具体资料请在官网海淘.TMS570LC4357资料 1. 下载软件 官网下载对应的编译编辑工具如下图&#xff0c;主要是这两个&#xff0c;其它Flash工具等酌情考虑 安装软件&#xff0c;一直next就可以。安装后新建工程 2. 新建工程 如果不知道怎么建工程&#xff0…...

工作记录 2016-12-22

工作记录 2016-12-22 更新的问题 1、修改了Job Summary的Bill Amount的Bug。 2、修改了Account #的宽度。 3、修改了Clearinghouse Status的默认查询的条件。 4、修改了Upload Files的Add File的bug。 5、Pending Pool、Missing Infos加了Write Off&#xff0c;修改了Histor…...

若依-导出后端解析

针对若依框架微服务版本学习 若依导入导出功能的具体使用详见&#xff1a;后台手册 | RuoYi 1.导出逻辑&#xff1a; 导出文件的逻辑是先创建一个临时文件&#xff0c;等待前端请求下载结束后马上删除这个临时文件。但是有些下载插件&#xff0c;例如迅雷&#xff08;他们是二…...

【QT5 Widgets示例】记事本:(二)界面设计

文章目录 记事本&#xff1a;&#xff08;二&#xff09;界面设计设置窗口标题和图标创建菜单工具栏创建文本框 记事本&#xff1a;&#xff08;二&#xff09;界面设计 设置窗口标题和图标 标题 点击窗口&#xff0c;修改windowTitle项 图标 点击windowIcon 倒三角&#xf…...

MPPT与PWM充电原理及区别详解

MPPT&#xff08;最大功率点跟踪&#xff09;和PWM&#xff08;脉宽调制&#xff09;是太阳能充电控制器中常用的两种技术&#xff0c;它们在原理、效率和适用场景上有显著区别。以下是两者的详细对比&#xff1a; 1. 工作原理 PWM&#xff08;脉宽调制&#xff09; 核心机制…...

K8S 集群搭建——cri-dockerd版

目录 一、工作准备 1.配置主机名 2.配置hosts解析 3.配置免密登录&#xff08;只需要在master上操作&#xff09; 4.时间同步&#xff08;每台节点都要做&#xff0c;必做&#xff0c;否则可能会因为时间不同步导致集群初始化失败&#xff09; 5.关闭系统防火墙 6.配置…...

使用express创建服务器保存数据到mysql

创建数据库和表结构 CREATE DATABASE collect;USE collect;CREATE TABLE info (id int(11) NOT NULL AUTO_INCREMENT,create_date bigint(20) DEFAULT NULL COMMENT 时间,type varchar(20) DEFAULT NULL COMMENT 数据分类,text_value text COMMENT 内容,PRIMARY KEY (id) ) EN…...

ubuntu-学习笔记-nginx+php

nginxphp nginx下载nginx配置nginx.conf php其他 记录一下在ubuntu中nginxphp部署tp项目 nginx nginx就是正常下载 下载nginx sudo apt-get install nginx tp项目版本是3.2&#xff0c;通过设置路由&#xff0c;以域名/api.php/控制器/xxx的格式进行api的调用&#xff0c;文…...

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

DeepSeek AI 聊天助手集成指南 先看完整效果&#xff1a; PixPin_2025-02-19_09-15-59 效果图&#xff1a; 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…...

github生成badges的方法

在Github页面上生成类似下面这样的badge的方法 你可以通过以下步骤在GitHub个人主页的README中创建类似的技术栈徽章&#xff1a; 一、使用 Shields.io 生成徽章 Shields.io 是一个开源徽章生成工具&#xff0c;支持自定义文本、颜色、图标等参数。 1. 基础模板 https://…...

vulnhub靶场渗透之SickOs1.2渗透教程,计划任务提权、chkrootkit提权

vulnhub靶场渗透之SickOs1.2渗透教程&#xff0c;计划任务提权、chkrootkit提权 一、信息收集 1、首先拿到靶场先扫一下ip 2025.3.7 AM 8&#xff1a;36 arp-scan -l 扫描同网段 nmap -sP 192.168.66.24/02、指纹扫描 nmap -sS -sV 192.168.66.130 指纹扫描PORT STATE S…...

Ubuntu系统部署.NET 8网站项目

一、使用XShell连接 Ubuntu系统初次连接时默认的用户名为&#xff1a;ubuntu&#xff0c;使用此用户名与系统登录密码进行连接。 登录成功效果如下图&#xff1a; 二、root用户登录 linux下有超级用户&#xff08;root&#xff09;和普通用户&#xff0c;普通用户不能直接操…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程

背景&#xff1a; 实现设想&#xff1a; 要创建自动化发布&#xff0c;需要准备一台测试服务器提前安装好java运行所需的环境&#xff0c;JDK版本最好和Windows开发机器上的版本一致&#xff0c;在Jenkins上配置将构建好的jar上传到测试服务器上&#xff0c;测试服务器自动启动…...

【Academy】Web 缓存欺骗 ------ Web cache deception

Web 缓存欺骗 ------ Web cache deception 1. 概述2. Web 缓存2.1 缓存键2.2 缓存规则 3. 构建 Web 缓存欺骗攻击3.1 使用缓存破坏器3.2 检测缓存的响应 4. 利用静态扩展缓存规则4.1 路径映射差异4.2 利用路径映射差异4.3 分隔符差异4.4 利用分隔符差异4.5 分隔符解码差异4.6 利…...

MATLAB表格Table与时间序列Timetable的高效操作方法

MATLAB中的表格&#xff08;Table&#xff09; 和 时间序列&#xff08;Timetable&#xff09; 是处理结构化数据和时间相关数据的核心工具。以下从基础操作到高级技巧&#xff0c;分步骤详解其使用方法。 一、创建与基础操作 1. 表格&#xff08;Table&#xff09;的创建与访…...

【leetcode hot 100 21】合并两个有序链表

解法一&#xff1a;新建一个链表存放有序的合并链表。当list1和list2至少有一个非空时&#xff0c;返回非空的&#xff1b;否则找出两个链表的最小值作为新链表的头&#xff0c;然后依次比较两链表&#xff0c;每次都先插入小的值。 /*** Definition for singly-linked list.*…...

本地部署 OpenManus 保姆级教程(Windows 版)

一、环境搭建 我的电脑是Windows 10版本&#xff0c;其他的没尝试&#xff0c;如果大家系统和我的不一致&#xff0c;请自行判断&#xff0c;基本上没什么大的出入啊。 openManus的Git地址&#xff1a;https://github.com/mannaandpoem/OpenManus 根据官网的两种安装推荐方式如…...

20250310:OpenCV mat对象与base64互转

代码: https://github.com/ReneNyffenegger/cpp-base64 指南:https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp/ 实操:...

WPS的付费功能,这款软件可完美平替

因为作者有工作上的需求加上WPS使用批量提取图片需要会员&#xff0c;所以自己使用cursor制作了一个从excel中提取图片的工具。 支持提取Excel中的浮动图片和根据图片链接来下载图片。 Excel Image Extractor Excel图片提取工具 软件的功能非常强大&#xff0c;支持提取Excel中…...

L1-088 静静的推荐

L1-088 静静的推荐 - 团体程序设计天梯赛-练习集 (pintia.cn) 题解 这里代码很简单&#xff0c;但是主要是循环里面的内容很难理解&#xff0c;下面是关于循环里面的内容理解&#xff1a; 这里 n 10 表示有 10 个学生&#xff0c;k 2 表示企业接受 2 批次的推荐名单&#…...

NS3学习——运行自定义拥塞控制算法步骤

目录 一、添加优化后的代码文件 二、更改CMakeLists文件中内容 三、重新配置和编译ns-3 四、常见问题 目的&#xff1a;想在tcp拥塞控制算法的基础上进行优化改进&#xff0c;之后在ns3中运行优化后的算法&#xff0c;即自定义拥塞控制算法&#xff1b; 以tcpVegas算法为…...

前端开发中的常见设计模式:全面解析与实践

1. 引言 1.1 设计模式的重要性 设计模式是软件开发中经过验证的解决方案&#xff0c;能够帮助开发者解决常见的设计问题。在前端开发中&#xff0c;合理使用设计模式可以提高代码的可维护性、可扩展性和复用性。 1.2 本文的目标 本文旨在全面解析前端开发中常见的设计模式&…...

VSCode 2025最新 前端开发必备插件推荐汇总(提效指南)

&#x1f31f;前言: 如果你是一名前端开发工程师&#xff0c;合适的开发工具能大大提高工作效率。Visual Studio Code (VSCode) 凭借其轻量级、高扩展性的特点&#xff0c;已成为众多前端开发者在win系电脑的首选IDE。 名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。—…...

【redis】redis的单线程模型为什么效率高?

文章目录 单线程模型单线程的 Redis 为什么效率高&#xff1f;速度快&#xff1f;IO多路复用epoll 单线程模型 Redis 只使用一个线程&#xff0c;处理所有的命令请求。不是说一个 Redis 服务器进程内部只有一个线程&#xff0c;其实也有多个线程&#xff0c;多个线程是在处理网…...

scala的集合

scala的集合系统的区分了可变&#xff08; mutable &#xff09;和不可变&#xff08;immutable &#xff09;集合。 mkString(seq:String)&#xff1a;方法是将原字符串使用特定的字符串seq分割。 mkString(statrt:String,seq:String,end:String)&#xff1a;方法是将原字符…...

Flink状态管理深度探索:从Keyed State到分布式快照

Flink状态管理深度探索:从Keyed State到分布式快照 在大数据实时计算领域,Apache Flink凭借其精准的状态管理能力成为行业标杆。本文将从状态管理的核心机制出发,结合金融行业PB级数据处理实践,深入解析状态后端、容错机制与大规模优化策略。 一、Flink状态管理核心架构 …...

Android 11 DAC和MAC

在 Android 11 中,DAC(Discretionary Access Control,自主访问控制) 和 MAC(Mandatory Access Control,强制访问控制) 是两种不同的访问控制机制,主要用于 权限管理、安全性 以及 进程间访问控制。 1. DAC(自主访问控制) DAC(Discretionary Access Control,自主访…...

平衡二叉树(AVL树)

平衡二叉树是啥我就不多说了&#xff0c;本篇博客只讲原理与方法。 首先引入平衡因子的概念。平衡因子&#xff08;Balance Factor&#xff09;&#xff0c;以下简称bf。 bf 右子树深度 - 左子树深度。平衡结点的平衡因子可为&#xff1a;-1&#xff0c;0&#xff0c;1。除此…...

SSM架构 +java后台 实现rtsp流转hls流,在前端html上实现视频播放

序言&#xff1a;书接上文&#xff0c;我们继续 SSM架构 NginxFFmpeg实现rtsp流转hls流&#xff0c;在前端html上实现视频播放 步骤一&#xff1a;把rtsp流转化为hls流&#xff0c;用Java代码进行转换 package com.tools;import java.io.BufferedReader; import java.io.IOExc…...

贪心算法--

1.柠檬水找零 link:860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 贪心算法&#xff0c; 优先花出大面额bill&#xff0c; 尽可能保护小面额billint five 0, ten 0;// 不…...

【从0到1搞懂大模型】神经网络的实现:数据策略、模型调优与评估体系(3)

一、数据集的划分 &#xff08;1&#xff09;按一定比例划分为训练集和测试集 我们通常取8-2、7-3、6-4、5-5比例切分&#xff0c;直接将数据随机划分为训练集和测试集&#xff0c;然后使用训练集来生成模型&#xff0c;再用测试集来测试模型的正确率和误差&#xff0c;以验证…...

CTF工具集合-持续更新

工具地址https://github.com/huan-cdm/ctf_tools工具介绍&#xff1a; 1.ARCHPR&#xff1a;压缩包密码破解工具 2.StegSolve-1.4.jar&#xff1a;隐写图片查看工具 3.ctf_decrypt_tool.rar&#xff1a;随波逐流CTF编码工具 4.010_Editor_All_Versions_For_Windows_CracKed.…...

小方摄像头接入本地服务器的方法

最早众筹时买了几个小方摄像头&#xff0c;后来嫌弃分辨率&#xff0c;就淘汰吃灰好几年&#xff0c;最近想折腾个摄像头识别的小项目&#xff0c;秉着不投入先凑合跑起来的原则&#xff0c;想到了尘封已久的小方&#xff0c;想看看能不能通过网络拉取数据流。 搜索了下&#x…...

取反符号~

取反符号 ~ 用于对整数进行按位取反操作。它会将二进制表示中的每一位取反&#xff0c;即 0 变 1&#xff0c;1 变 0。 示例 a 5 # 二进制表示为 0000 0101 b ~a # 按位取反&#xff0c;结果为 1111 1010&#xff08;补码表示&#xff09; print(b) # 输出 -6解释 5 的二…...

Jenkins实现自动化构建与部署:上手攻略

一、持续集成与Jenkins核心价值 1.1 为什么需要自动化构建&#xff1f; 在现代化软件开发中&#xff0c;团队每日面临以下挑战&#xff1a; 高频代码提交&#xff1a;平均每个开发者每天提交5-10次代码。多环境部署&#xff1a;开发、测试、预发布、生产环境需频繁同步。复杂…...

爱普生温补晶振 TG5032CFN高精度稳定时钟的典范

在科技日新月异的当下&#xff0c;众多领域对时钟信号的稳定性与精准度提出了极为严苛的要求。爱普生温补晶振TG5032CFN是一款高稳定性温度补偿晶体振荡器&#xff08;TCXO&#xff09;。该器件通过内置温度补偿电路&#xff0c;有效抑制环境温度变化对频率稳定性的影响&#x…...