Electron学习+打包

news2025/7/18 11:16:47

1. 什么是 Electron?

Electron 是⼀个 跨平台桌⾯应⽤ 开发框架,开发者可以使⽤:HTML、CSS、JavaScript 等
Web 技术来构建桌⾯应⽤程序,它的本质是结合了 Chromium Node.js ,现在⼴泛⽤于桌⾯应
⽤程序开发,例如这写桌⾯应⽤都⽤到了 Electron 技术:
        1、VisualStudioCode
        2、GitHubDesktop
        3、1Password
        4、新版 QQ

2. Electron 的优势

1. 可跨平台:同⼀套代码可以构建出能在:Windows、macOS、Linux 上运⾏的应⽤程序。
2. 上⼿容易:使⽤ Web 技术就可以轻松完成开发桌⾯应⽤程序。
3. 底层权限:允许应⽤程序访问⽂件系统、操作系统等底层功能,从⽽实现复杂的系统交互。
4. 社区⽀持:拥有⼀个庞⼤且活跃的社区,开发者可以轻松找到⽂档、教程和开源库

3. Electron 技术架构

3.1. 技术架构

3.2. 进程模型

此处我们只是先了解⼀下进程模型,后⾯会详细讲解。

4. 搭建⼀个⼯程

初始化⼀个包,并提填写好 package.json 中的必要信息及启动命令。
{
 "name": "test",
 "version": "1.0.0",
 "main": "main.js",
 "scripts": {
 "start": "electron ." //start命令⽤于启动整个应⽤
 },
 "author": "tianyu", //为后续能顺利打包,此处要写明作者。
 "license": "ISC",
 "description": "this is a electron demo", //为后续能顺利打包,此处要编写描述。
}
安装 electron 作为开发依赖。
npm i electron -D

main.js 中编写代码,创建⼀个基本窗⼝

/*
 main.js运⾏在应⽤的主进程上,⽆法访问Web相关API,主要负责:控制⽣命周期、显示界⾯、
控制渲染进程等其他操作。
*/
const { app, BrowserWindow } = require('electron')
// ⽤于创建窗⼝
function createWindow() {
 const win = new BrowserWindow({
 width: 800, // 窗⼝宽度
 height: 600, // 窗⼝⾼度
 autoHideMenuBar: true, // ⾃动隐藏菜单栏
 alwaysOnTop: true, // 置顶
 x: 0, // 窗⼝位置x坐标
 y: 0 // 窗⼝位置y坐标
 })
 // 加载⼀个远程⻚⾯
 win.loadURL('http://www.atguigu.com')
}
// 当app准备好后,执⾏createWindow创建窗⼝
app.on('ready',()=>{
 createWindow()
})
关于 BrowserWindow 的更多配置项,请参考: BrowserWindow实例属性
启动应⽤查看效果
npm start
效果如下:
额外解释:

Electron

客户端,桌面应用,安装在电脑里面用的,而不是手机里面用的,比如视频编辑和头像处理软件

这两个需要补全,否则无法打包

  • 入口点 应当是 main.js (您很快就会创建它)
  • authorlicense 和 description 可以是任何值,但在稍后的packaging中是必需的。

 初次打开项目,会有默认的文件

autoHideMenuBar: true //自动隐藏菜单栏

alwaysOnTop: true

一打开页面从左面的x:0,y:0开始

 ctrl+shift+i 调出控制台

 5. 加载本地⻚⾯

创建 pages/index.html 编写内容:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="UTF-8" />
 <title>index</title>
 </head>
 <body>
 <h1>你好啊!</h1>
 </body>
</html>

修改 mian.js 加载本地⻚⾯

// 加载⼀个本地⻚⾯
win.loadFile('./pages/index.html')
此时开发者⼯具会报出⼀个安全警告,需要修改 index.html ,配置 CSP(Content
Security-Policy)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; st
yle-src 'self' 'unsafe-inline'; img-src 'self' data:;">
上述配置的说明
1.default-src 'self'
default-src :配置加载策略,适⽤于所有未在其它指令中明确指定的资源类型。
self :仅允许从同源的资源加载,禁⽌从不受信任的外部来源加载,提⾼安全性。
2. style-src 'self' 'unsafe-inline'
style-src :指定样式表(CSS)的加载策略。
self :仅允许从同源的资源加载,禁⽌从不受信任的外部来源加载,提⾼安全性。
unsafe-inline :允许在HTML⽂档内使⽤内联样式。
3. img-src 'self' data:
img-src :指定图像资源的加载策略。
self :表示仅允许从同源加载图像。
data: :允许使⽤ data: URI 来嵌⼊图像。这种URI模式允许将图像数据直接嵌
⼊到HTML或CSS中,⽽不是通过外部链接引⽤。
关于 CSP 的详细说明请参考: MDN-Content-Security-Policy Electron Security

 6. 完善窗⼝⾏为

1. Windows 和 Linux 平台窗⼝特点是:关闭所有窗⼝时退出应⽤。
// 当所有窗⼝都关闭时
app.on('window-all-closed', () => {
 // 如果所处平台不是mac(darwin),则退出应⽤。
 if (process.platform !== 'darwin') app.quit()
})

2. mac 应⽤即使在没有打开任何窗⼝的情况下也继续运⾏,并且在没有窗⼝可⽤的情况下激活
应⽤时会打开新的窗⼝。
// 当app准备好后,执⾏createWindow创建窗⼝
app.on('ready',()=>{
 createWindow()
 // 当应⽤被激活时
 app.on('activate', () => {
 //如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
 if (BrowserWindow.getAllWindows().length === 0) createWindow()
 })
})

7. 配置⾃动重启

1. 安装 Nodemon
npm i nodemon -D
2. 修改 package.json 命令
scripts": {
 "start": "nodemon --exec electron ."
},
3. 配置 nodemon.json 规则
{
 "ignore": [
 "node_modules",
 "dist"
 ],
 "restartable": "r",
 "watch": ["*.*"],
 "ext": "html,js,css"
}
配置好以后,当代码修改后,应⽤就会⾃动重启了。

8. 主进程与渲染进程

 下图是 Chrome 浏览器的程序架构,图来⾃于Chrome 漫画

 Electron 应⽤的结构与上图⾮常相似,在 Electron 中主要控制两类进程:主进程、渲染器进程。

8.1. 主进程

每个 Electron 应⽤都有⼀个单⼀的主进程,作为应⽤程序的⼊⼝点。 主进程在 Node.js 环境中运
⾏,它具有 require 模块和使⽤所有 Node.js API 的能⼒,主进程的核⼼就是: 使用BrowserWindow来创建和管理窗口

8.2. 渲染进程

每个 BrowserWindow 实例都对应⼀个单独的渲染器进程,运⾏在渲染器进程中的代码,必须遵
守⽹⻚标准,这也就意味着: 渲染器进程无权直接访问 require 或使用任何 Node.js 的API
问题产⽣:处于渲染器进程的⽤户界⾯,该怎样才与 Node.js 和 Electron 的原⽣桌⾯功能进⾏
交互呢?

9. Preload 脚本

预加载(Preload)脚本是运⾏在渲染进程中的, 但它是在 ⽹⻚内容加载之前 执⾏的,这意味着它
具有⽐普通渲染器代码更⾼的权限,可以访问 Node.js 的 API,同时⼜可以与⽹⻚内容进⾏安全
的交互。
简单说:它是 Node.js 和 Web API 的桥梁,Preload 脚本可以安全地将部分 Node.js 功能暴露
给⽹⻚,从⽽减少安全⻛险。
需求:点击按钮后,在⻚⾯呈现当前的 Node 版本。
具体⽂件结构与编码如下:
1. 创建预加载脚本 preload.js ,内容如下:
const {contextBridge} = require('electron')
// 暴露数据给渲染进程
contextBridge.exposeInMainWorld('myAPI',{
 n:666,
 version:process.version
})
2.在主线程中引⼊ preload.js
const win = new BrowserWindow({
 /*******/
 webPreferences:{
 preload:path.resolve(__dirname,'./preload.js')
 }
 /*******/
})
3. 在 html ⻚⾯中编写对应按钮,并创建专⻔编写⽹⻚脚本的 render.js ,随后引⼊。
<body>
 <h1>你好啊!</h1>
 <button id="btn">在⽤户的D盘创建⼀个hello.txt</button>
 <script type="text/javascript" src="./render.js"></script>
</body>
4. 在渲染进程中使⽤ version
btn.addEventListener('click',()=>{
 console.log(myAPI.version)
 document.body.innerHTML += `<h2>${myAPI.version}</h2>`
})
5. 整体⽂件结构如下:

 渲染进程(n个)与主进程(1个,node环境下运行)

预加载脚本是在渲染进程执行的,只能执行一部分api

预加载先执行,渲染进程后执行

preload.js

contextBridge.exposeInMainWorld('abc',{

  xyz:100

})

预加载脚本不能访问__dirname,会报错

preload.js

contextBridge.exposeInMainWorld('myAPI',{

  version: process.version,

  xyz: __dirname

})

render.js

btn1.onclick = () =>{

  // alert('你点我了',process.version)

  console.log(myAPI.xyz);  //报错如下

}

10. 进程通信(IPC)

值得注意的是:
上⽂中的 preload.js ,⽆法使⽤全部 Node API ,⽐如:不能使⽤ Node 中的 fs
块,但主进程( main.js )是可以的,这时就需要
了。简单说:要
preload.js 通知 main.js 去调⽤ fs 模块去⼲活。
关于 Electron 进程通信,我们要知道:
  • IPC 全称为: InterProcess Communication ,即:进程通信。
  • IPC Electron 中最为核⼼的内容,它是从 UI 调⽤原⽣ API 的唯⼀⽅法!
  • Electron 中,主要使⽤ ipcMainipcRenderer来定义“通道”,进⾏进程通信。

10.1. 渲染进程➡️主进程(单向)

概述: 渲染器进程 ipcRenderer.send 发送消息,在 主进程 中使⽤ ipcMain.on 接收消息。
常⽤于: 在Web中调用主进程的API ,例如下⾯的这个需求:
需求:点击按钮后,在⽤户的 D 盘创建⼀个 hello.txt ⽂件,⽂件内容来⾃于⽤户输⼊。

渲染进程向主进程单向通信

1. ⻚⾯中添加相关元素, render.js 中添加对应脚本

index.html

<input id="content" type="text"><br><br>
<button id="btn">在⽤户的D盘创建⼀个hello.txt</button>

 render.js

const btn = document.getElementById('btn')
const content = document.getElementById('content')
btn.addEventListener('click',()=>{
 console.log(content.value)
 myAPI.saveFile(content.value)
})

 2. preload.js 中使⽤ ipcRenderer.send('信道',参数) 发送消息,与主进程通信。

preload.js

const {contextBridge,ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('myAPI',{
 /*******/
 saveFile(str){
 // 渲染进程给主进程发送⼀个消息
 ipcRenderer.send('create-file',str)
 }
})
3. 主进程中,在加载⻚⾯之前,使⽤ ipcMain.on(' 信道 ', 回调 ) 配置对应回调函数,接收
消息。
main.js
// ⽤于创建窗⼝
function createWindow() {
 /**********/
 // 主进程注册对应回调
 ipcMain.on('create-file',createFile)
 // 加载⼀个本地⻚⾯
 win.loadFile(path.resolve(__dirname,'./pages/index.html'))
}
//创建⽂件
function createFile(event,data){
 fs.writeFileSync('D:/hello.txt',data)
}

写入成功

 10.2. 渲染进程主进程(双向)

概述: 渲染进程 通过 ipcRenderer.invoke 发送消息, 主进程 使⽤ ipcMain.handle 接收并 处理消
息。
备注: ipcRender.invoke 的返回值是 Promise 实例。
常⽤于: 从渲染器进程调用主进程方法并等待 ,例如下⾯的这个需求:
需求:点击按钮从 D 盘读取 hello.txt 中的内容,并将结果呈现在⻚⾯上。
1. ⻚⾯中添加相关元素, render.js 中添加对应脚本
index.html
<button id="btn">读取⽤户D盘的hello.txt</button>

render.js

const btn = document.getElementById('btn')
btn.addEventListener('click',async()=>{
 let data =
 document.body.innerHTML += `<h2>${data}</h2>`
})
2. preload.js 中使⽤ ipcRenderer.invoke(' 信道 ', 参数 ) 发送消息,与主进程通信。
const {contextBridge,ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('myAPI',{
 /*******/
 readFile (path){
 return ipcRenderer.invoke('read-file')
 }
})
3. 主进程中,在加载⻚⾯之前,使⽤ ipcMain.handle(' 信道 ', 回调 ) 接收消息,并配置回
调函数。
// ⽤于创建窗⼝
function createWindow() {
 /**********/
 // 主进程注册对应回调
 ipcMain.handle('read-file',readFile)
 // 加载⼀个本地⻚⾯
 win.loadFile(path.resolve(__dirname,'./pages/index.html'))
}
//读取⽂件
function readFile(event,path){
 return fs.readFileSync(path).toString()
}

10.3. 主进程到➡️渲染进程

概述: 主进程 使⽤ win.webContents.send 发送消息, 渲染进程 通过 ipcRenderer.on 处理消息,
常⽤于: 从主进程主动发送消息给渲染进程 ,例如下⾯的这个需求:
需求:应⽤加载 6 秒钟后,主动给渲染进程发送⼀个消息,内容是:你好啊!
1. ⻚⾯中添加相关元素, render.js 中添加对应脚本
render.js
window.onload = ()=>{
 myAPI.getMessage(logMessage)
}
function logMessage(event,str){
 console.log(event,str)
}
2. preload.js 中使⽤ ipcRenderer. on (' 信道 ', 回调 ) 接收消息,并配置回调函数。
prelosd.js
const {contextBridge,ipcRenderer} = require('electron')
contextBridge.exposeInMainWorld('myAPI',{
 /*******/
 getMessage: (callback) => {
 return ipcRenderer.on('message', callback);
 }
})
3. 主进程中,在合适的时候,使⽤ win.webContents.send(' 信道 ', 数据 ) 发送消息。
main.js
// ⽤于创建窗⼝
function createWindow() {
 /**********/
 // 加载⼀个本地⻚⾯
 win.loadFile(path.resolve(__dirname,'./pages/index.html'))
 // 创建⼀个定时器
 setTimeout(() => {
 win.webContents.send('message','你好啊!')
 }, 6000);
}

11. 打包应⽤

使⽤ electron-builder 打包应⽤
1. 安装 electron-builder
npm install electron-builder -D
2. package.json 中进⾏相关配置,具体配置如下:
备注:json ⽂件不⽀持注释,使⽤时请去掉所有注释
{
 "name": "video-tools", // 应⽤程序的名称
 "version": "1.0.0", // 应⽤程序的版本
 "main": "main.js", // 应⽤程序的⼊⼝⽂件
 "scripts": {
 "start": "electron .", // 使⽤ `electron .` 命令启动应⽤程序
 "build": "electron-builder" // 使⽤ `electron-builder` 打包应⽤程序,⽣成
安装包
 },
 "build": {
 "appId": "com.atguigu.video", // 应⽤程序的唯⼀标识符
 // 打包windows平台安装包的具体配置
 "win": {
 "icon":"./logo.ico", //应⽤图标
 "target": [
 {
 "target": "nsis", // 指定使⽤ NSIS 作为安装程序格式
 "arch": ["x64"] // ⽣成 64 位安装包
 }
 ]
 },
 "nsis": {
 "oneClick": false, // 设置为 `false` 使安装程序显示安装向导界⾯,⽽不是⼀
键安装
 "perMachine": true, // 允许每台机器安装⼀次,⽽不是每个⽤户都安装
 "allowToChangeInstallationDirectory": true // 允许⽤户在安装过程中选择
安装⽬录
 }
 },
 "devDependencies": {
 "electron": "^30.0.0", // 开发依赖中的 Electron 版本
 "electron-builder": "^24.13.3" // 开发依赖中的 `electron-builder` 版本
 },
 "author": "tianyu", // 作者信息
 "license": "ISC", // 许可证信息
 "description": "A video processing program based on Electron" // 应⽤程
序的描述
}

3. 执⾏打包命令

npm run build

12. electron-vite

electron-vite 是⼀个新型构建⼯具,旨在为 Electron 提供更快、更精简的体验。主要由五部分
组成:
⼀套构建指令,它使⽤ Vite 打包你的代码,并且它能够处理 Electron 的独特环境,包括
Node.js 和浏览器环境。
集中配置主进程、渲染器和预加载脚本的 Vite 配置,并针对 Electron 的独特环境进⾏预配
置。
为渲染器提供快速模块热替换(HMR)⽀持,为主进程和预加载脚本提供热重载⽀持,极⼤
地提⾼了开发效率。
优化 Electron 主进程资源处理。
使⽤ V8 字节码保护源代码。
electron-vite 快速、简单且功能强⼤,旨在开箱即⽤。
官⽹地址: https://cn-evite.netlify.app/

扩展 

渲染进程与渲染进程通信借助于主进程

总体代码:

main.js

console.log('main');
// BrowserWindow ⽤于创建窗⼝
const { app, BrowserWindow, ipcMain } = require("electron");
const path = require('path')
const fs = require('fs')

function writeFile(_,data){
  console.log(_,data);
  fs.writeFileSync('D:/hello.txt',data)
}
function readFile(){
  const res = fs.readFileSync('D:/hello.txt').toString()
  // console.log('###', res);
  return res
}
const createWindow = () => {
  // ⽤于创建窗⼝
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    autoHideMenuBar: true,
    // x: 0,
    // y: 0,
    // alwaysOnTop: true,
    webPreferences:{
      preload:path.resolve(__dirname,'./preload.js')
    }
  });
  ipcMain.on('file-save', writeFile),
  ipcMain.handle('file-read', readFile),
  //win.loadURL('http://www.atguigu.com')
  win.loadFile("./pages/index.html");
};
// console.log(process.version);  //node
// console.log(process.versions.chrome);  //chrome
// console.log(process.versions.node);  //node
// console.log(process.versions.electron);  //electron
// 当app准备好后,执⾏createWindow创建窗⼝
app.on("ready", () => {
  console.log('应用准备完毕了');
  createWindow()
  // 当应⽤被激活时
  app.on("activate", () => {
    // 如果当前应⽤没有窗⼝,则创建⼀个新的窗⼝
    if (BrowserWindow.getAllWindows().length === 0) createWindo();
  });
});
// 当所有窗⼝都关闭时(Windows & Linux)
app.on("window-all-closed", () => {
  // // 如果所处平台不是mac(darwin),则退出应⽤, app.quit()
  if (process.platform !== "darwin") app.quit();
});

preload.js

// 预加载教程,作为主进程与渲染进程的桥梁
console.log('preload',process.version);
const {contextBridge,ipcRenderer} = require('electron')

contextBridge.exposeInMainWorld('myAPI',{
  version: process.version,
  // xyz: __dirname //报错
  saveFile:(data)=>{
    // ipcRenderer.send(信道, 数据)
    ipcRenderer.send('file-save', data) //对应主进程的on
  },
  readFile(){
    // invoke的返回值永远是promise
    // let x = await ipcRenderer.invoke('file-read') //对应主进程的handle
    // console.log('@@@@@@',x);
    return ipcRenderer.invoke('file-read') //对应主进程的handle
  }
})


nodemon.json

{
  "ignore": [
  "node_modules",
  "dist"
  ],
  "restartable": "r",
  "watch": ["*.*"],
  "ext": "html,js,css"
 }

package.json

{
  "name": "electron_test",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "nodemon --exec electron .",
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "electron-builder"
  },
  "build": {
    "appId": "com.atguigu.video",
    "win": {
      "icon": "./logo.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ]
    },
    "nsis": {
      "oneClick": false,
      "perMachine": true,
      "allowToChangeInstallationDirectory": true
    }
  },
  "author": "wen",
  "license": "ISC",
  "description": "this is a electron demo",
  "devDependencies": {
    "electron": "^36.1.0",
    "electron-builder": "^26.0.12",
    "nodemon": "^3.1.10"
  }
}

pages/render.js

console.log('render');
const btn1 = document.getElementById('btn1')
const btn2 = document.getElementById('btn2')
const btn3 = document.getElementById('btn3')
const input = document.getElementById('input')
btn1.onclick = () =>{
  // alert('你点我了',process.version)
  console.log(myAPI.version);
}
btn2.onclick = () =>{
  // 文件内容是input.value,输入什么就是什么
  // 调用之后就找主进程,通过预加载脚本来找
  myAPI.saveFile(input.value)
}
btn3.onclick = async () =>{
  let data = await myAPI.readFile(input.value)
  alert(data)
}

pages/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;">
  <link rel="stylesheet" href="./index.css">
  <title>index</title>
</head>
<body>
  <h1>欢迎学习Electron开发!!!!!</h1>
  <button id="btn1">点我</button>
  <br/>
  <br/>
  <hr>
  <input id="input" type="text">
  <button id="btn2">向D盘写入hello.txt</button>
  <br>
  <br>
  <hr>
  <button id="btn3">读取D盘中的hello.txt</button>
  <script type="text/javascript" src="./render.js"></script>
</body>
</html>

pages/index.css

h1{
  background-color: gray;
  color: orange;
}

打包之后文件 

双击.exe文件可以安装 

安装之后的文件

 

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

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

相关文章

NumPy线性代数功能全解析:矩阵运算与方程求解实用指南

NumPy 是线性代数领域中高效的工具。它可以帮助完成矩阵运算和方程求解。本文将介绍 NumPy 中用于线性代数的常用函数。 矩阵乘法 矩阵乘法会根据两个矩阵生成一个新矩阵。具体做法是将第一个矩阵的每一行与第二个矩阵的每一列相乘&#xff0c;并将乘积相加&#xff0c;得到新…

《RabbitMQ 全面解析:从原理到实战的高性能消息队列指南》

一、RabbitMQ 核心原理与架构 1. 核心组件与工作流程 RabbitMQ 基于 AMQP 协议&#xff0c;核心组件包括 生产者&#xff08;Producer&#xff09;、交换机&#xff08;Exchange&#xff09;、队列&#xff08;Queue&#xff09; 和 消费者&#xff08;Consumer&#xff09;。…

Android Framework学习二:Activity创建及View绘制流程

文章目录 Window绘制流程Window Manager Service&#xff08;WMS&#xff09;SurfaceSurfaceFlinger 安卓View层次结构ActivityPhoneWindowActivity与PhoneWindow两者之间的关系ViewRootImplDecorViewDecorView 的作用DecorView 的结构总结 Activity创建流程View invalidate调用…

python如何在深度学习框架目标检测算法使用Yolov8训练道路汽车漆面车漆缺陷数据集 建立基于YOLOv8道路汽车漆面缺陷(划痕)检测系统

基于YOLOv8道路汽车漆面缺陷&#xff08;划痕&#xff09;检测系统 文章目录 1. 安装依赖2. 数据集准备与划分3. 数据预处理4. 配置YOLOv85. 训练和评估模型6. 推理与可视化7. 构建GUI应用程序 道路汽车漆面车漆缺陷检测数据集1221张 1类 汽车漆面缺陷检测YOLO数据集 1221张…

高性能、云原生的对象存储服务MinIO 详细介绍与案例应用

什么是MinIO&#xff1f; MinIO是一个高性能、云原生的对象存储服务&#xff0c;采用Apache License v2.0开源协议发布。它与Amazon S3云存储服务API兼容&#xff0c;适合构建高性能、可扩展的存储基础设施。支持大规模非结构化数据的存储&#xff0c;适合图片、视频、日志、备…

Arduino按键开关编程详解

一、按键开关的基本原理与硬件连接 1.1 按键开关的工作原理 按键开关是一种常见的输入设备&#xff0c;其核心原理基于机械触点的闭合与断开。当用户按下按键时&#xff0c;内部的金属片会连接电路两端&#xff0c;形成通路&#xff1b;松开按键后&#xff0c;金属片在弹簧作…

鸢尾花(Iris)数据集的多模型分类与可视化分析工具

该程序是一个鸢尾花(Iris)数据集的多模型分类与可视化分析工具,主要功能如下: 1. 数据加载与预处理 功能说明: 使用sklearn.datasets.load_iris()加载经典的鸢尾花数据集。将数据转为pandas.DataFrame,并将类别数字标签映射为中文类别名(山鸢尾、变色鸢尾、维吉尼亚鸢尾…

[蓝桥杯 2023 国 Python B] 划分 Java

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[] arr new int[41];int sum 0;for (int i 1; i < 40; i) {arr[i] sc.nextInt();sum arr[i];}sc.close();int target sum / 2; // 最接近的两…

25.4.30数据结构|并查集 路径压缩

书接上回 上一节&#xff1a;数据结构|并查集 前言 &#xff08;一&#xff09;理论理解&#xff1a; 1、在QuickUnion快速合并的过程中&#xff0c;每次都要找根ID&#xff0c;而路径压缩让找根ID变得更加迅速直接。 2、路径压缩 针对的是findRootIndex()【查找根ID】进行的压…

MATLAB R2024a安装教程

安装步骤&#xff1a; 软件大小&#xff1a;约12.08G 安装环境&#xff1a;Win10~Win11或更高 下载好安装包&#xff0c;可以在网上找个安装包&#xff0c;比如我用国内镜像matlab地址github.com/futureflsl/matlab-chinese-mirror&#xff0c;这样下载稍微快点 1.开始安装…

WEB安全--社会工程--SET钓鱼网站

1、选择要钓鱼的网站 2、打开kali中的set 3、启动后依次选择&#xff1a; 4、输入钓鱼主机的地址&#xff08;kali&#xff09;和要伪装的网站域名&#xff1a; 5、投放钓鱼网页&#xff08;服务器域名:80&#xff09; 6、获取账号密码

Java学习手册:Spring 数据访问

一、Spring JDBC JdbcTemplate &#xff1a;Spring JDBC 提供了 JdbcTemplate 类&#xff0c;它简化了数据库操作&#xff0c;提供了丰富的 API 来执行数据库访问任务。JdbcTemplate 可以自动处理数据库连接的获取、释放&#xff0c;SQL 语句的执行&#xff0c;结果集的处理等…

linux 使用nginx部署next.js项目,并使用pm2守护进程

前言 本文基于&#xff1a;操作系统 CentOS Stream 8 使用工具&#xff1a;Xshell8、Xftp8 服务器基础环境&#xff1a; node - 请查看 linux安装node并全局可用pm2 - 请查看 linux安装pm2并全局可用nginx - 请查看 linux 使用nginx部署vue、react项目 所需服务器基础环境&…

阿里云服务迁移实战: 07-其他服务迁移

概述 当完成了服务器、数据库、IP、OSS等迁移后&#xff0c;剩下的就是其他服务了。 短信网关 短信模板只能一个个创建&#xff0c;不能批量操作。但是可以使用以下方式优化操作。 在原账号导出模板列表 概述 当完成了服务器、数据库、IP、OSS等迁移后&#xff0c;剩下的…

uniapp 实现低功耗蓝牙连接并读写数据实战指南

在物联网应用场景中&#xff0c;低功耗蓝牙&#xff08;BLE&#xff09;凭借其低能耗、连接便捷的特点&#xff0c;成为设备间数据交互的重要方式。Uniapp 作为一款跨平台开发框架&#xff0c;提供了丰富的 API 支持&#xff0c;使得在多个端实现低功耗蓝牙功能变得轻松高效。本…

【Java学习笔记】递归

递归&#xff08;recursion&#xff09; 思想&#xff1a;把一个复杂的问题拆分成一个简单问题和子问题&#xff0c;子问题又是更小规模的复杂问题&#xff0c;循环往复 本质&#xff1a;栈的使用 递归的注意事项 &#xff08;1&#xff09;需要有递归出口&#xff0c;否者就…

使用vue的插值表达式渲染变量,格式均正确,但无法渲染

如图&#xff0c;作者遇到的问题为&#xff0c;输入以下代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

leetcode 977. Squares of a Sorted Array

题目描述 双指针法一 用right表示原数组中负数和非负数的分界线。 nums[0,right-1]的是负数&#xff0c;nums[right,nums.size()-1]是非负数。 然后用合并两个有序数组的方法。合并即可。 class Solution { public:vector<int> sortedSquares(vector<int>&…

llamafactory-cli webui启动报错TypeError: argument of type ‘bool‘ is not iterable

一、问题 在阿里云NoteBook上启动llamafactory-cli webui报错TypeError: argument of type ‘bool’ is not iterable This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run gradio deploy from the terminal in the working directory t…

机器学习——特征选择

特征选择算法总结应用 特征选择概述 注&#xff1a;关于详细的特征选择算法介绍详见收藏夹。