electron基础使用

news2024/11/17 3:48:11

安装以及运行

当前node版本18+,按照官网提供操作,npm init进行初始化操作,将index.js修改为main.js,执行npm install --save-dev electron。(这里我挂梯子下载成功了。),添加如下代码至package.json

  "scripts": {
    "start": "electron ."
  },

新建一个index.html文件,内容随意。新建一个main.js文件,内容如下。主要意思是将一个界面加载到一个应用窗口中。createWindow()方法来将index.html加载进一个新的BrowserWindow实例。在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 可以通过使用 app.whenReady() API来监听此事件。 在whenReady()成功后调用createWindow()函数来打开窗口。

  • app 模块,它控制应用程序的事件生命周期。
  • BrowserWindow 模块,它创建和管理应用程序 窗口。
const { app, BrowserWindow } = require("electron");

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
  });

  win.loadFile("index.html");
};

app.whenReady().then(() => {
  createWindow();
});

添加关闭应用窗口事件,若输出有乱码则参考该博客输出乱码解决

app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    console.log("关闭");
    app.quit();
  }
});

在这里插入图片描述
添加代码至BrowserWindow中,可以打开控制台操作,应用开启后可以使用ctrl+shift+i打开控制台

    webContents: {
      openDevTools: true,
    },

在这里插入图片描述

vue3中运行

npm create @quick-start/electron

预加载脚本

Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境。 除了 Electron 模组 之外,您也可以访问 Node.js 内置模块 和所有通过 npm 安装的包。 另一方面,出于安全原因,渲染进程默认跑在网页页面上,而并非 Node.js里。

主进程负责管理整个应用程序的生命周期,包含创建窗口,运行在nodejs环境中,每次打开一个窗口,实际是创建一个渲染进程。主进程和渲染进程之间通过IPC通信。

为了将 Electron 的不同类型的进程桥接在一起,我们需要使用被称为 预加载 的特殊脚本。

BrowserWindow 的预加载脚本运行在具有 HTML DOM 和 Node.js、Electron API 的有限子集访问权限的环境中。预加载脚本默认 沙盒化不再拥有完整 Node.js 环境的访问权。 实际上,这意味着你只拥有一个 polyfilled 的 require 函数,这个函数只能访问一组有限的 API。
在这里插入图片描述
预加载脚本在渲染器加载网页之前注入。 如果你想为渲染器添加需要特殊权限的功能,可以通过 contextBridge 接口定义 全局对象。

创建preload.js文件,该脚本通过 versions 这一全局变量,将 Electron 的 process.versions 对象暴露给渲染器。

const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,我们也可以暴露变量
})

将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。webPreferences对象是用来定制渲染进程中的特性,其中preload属性是在网页运行其他脚本之前执行的脚本,用来提前注入Nodejs api或者其他需要再渲染进程中运行的脚本。

const { app, BrowserWindow } = require('electron')
const path = require('node:path')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.loadFile('index.html')
}

app.whenReady().then(() => {
  createWindow()
})

现在渲染器能够全局访问 versions 了,但是还需要将版本等数据渲染到界面中,可以通过window.versions访问,也可以很简单地使用 versions 来访问。 新建一个 renderer.js 脚本, 使用 document.getElementById DOM API 来替换 id 属性为 info 的 HTML 元素的文本。

const information = document.getElementById('info')
information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`

然后再index.html文件中引入

  <script src="./renderer.js"></script>

在这里插入图片描述

进程之间通信(简略)

Electron 的主进程和渲染进程有着清楚的分工并且不可互换,无论是从渲染进程直接访问 Node.js 接口,亦或者是从主进程访问 HTML 文档对象模型 (DOM),都是不可能的。
解决这一问题的方法是使用进程间通信 (IPC)。可以使用 Electron 的 ipcMain 模块 ipcRenderer 模块来进行进程间通信。 为了从你的网页向主进程发送消息,你可以使用ipcMain.handle设置一个主进程处理程序(handler),然后在预处理脚本中暴露一个被称为 ipcRenderer.invoke 的函数来触发该处理程序(handler)。

const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("versions", {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke("ping"),
});

主进程中设置你的 handle 监听器,在 HTML 文件加载之前完成了这些,所以才能保证在你从渲染器发送 invoke 调用之前处理程序能够准备就绪。
渲染主进程中引入const { app, BrowserWindow, ipcMain } = require("electron");

app.whenReady().then(() => {
  ipcMain.handle("ping", () => "pong");
  createWindow();
});

将发送器与接收器设置完成之后,现在你可以将信息通过刚刚定义的 ‘ping’ 通道从渲染器发送至主进程当中。修改renderer中的文件

const func = async () => {
  const response = await window.versions.ping()
  console.log(response) // 打印 'pong'
}

func()

在这里插入图片描述

打包程序

Electron 的核心模块中没有捆绑任何用于打包或分发文件的工具。 如果您在开发模式下完成了一个 Electron 应用,需要使用额外的工具来打包应用程序 (也称为可分发文件) 并分发给用户 。 可分发文件可以是安装程序 (例如 Windows 上的 MSI) 或者绿色软件 (例如 macOS 上的 .app 文件)。Electron Forge 是一个处理 Electron 应用程序打包与分发的一体化工具。 在工具底层,它将许多现有的 Electron 工具 (例如 @electron/packager、 @electron/osx-sign、electron-winstaller 等) 组合到一起。

安装打包工具npm install --save-dev @electron-forge/cli,然后输入npx electron-forge import等待初始化完毕,之后package会变为如下图所示
在这里插入图片描述

创建一个可分发版本

要创建可分发文件,请使用项目中的 make 脚本,该脚本最终运行了 electron-forge make 命令。npm run make注意如果npm init 的时候,author 与 description 可为任意值,但对于应用打包是必填项。如果没有则会出现如下错误
在这里插入图片描述
注意,项目打包路径中不能包含中文字符,否则报错。 打包成功如下
在这里插入图片描述

  1. npm run make它将首先运行 electron-forge package ,把您的应用程序 代码与 Electron 二进制包结合起来。
  2. 完成打包的代码将会被生成到一个特定的文件夹中。 然后它将使用这个文件夹为每个 maker 配置生成一个可分发文件。
    在这里插入图片描述
    打包完成后查看.exe文件运行。
    在这里插入图片描述

代码签名

为了将桌面应用程序分发给最终用户,我们 强烈建议 您对 Electron 应用进行 代码签名。 代码签名是交付桌面应用程序的重要组成部分,并且它对于应用程序的自动更新功能 (将会在教程最后部分讲解) 来说是必需的。

代码签名是一种可用于证明桌面应用程序是由已知来源创建的安全技术。 Windows 和 macOS 拥有其特定的代码签名系统,这将使用户难以下载或启动未签名的应用程序。代码签名在forge.config.js文件中添加

流程模型

多进程模型:在早期,浏览器通常使用单个进程来处理所有这些功能。 虽然这种模式意味着您打开每个标签页的开销较少,但也同时意味着一个网站的崩溃或无响应会影响到整个浏览器。Chrome 团队决定让每个标签页在自己的进程中渲染, 从而限制了一个网页上的有误或恶意代码可能导致的对整个应用程序造成的伤害。 然后用单个浏览器进程控制这些标签页进程,以及整个应用程序的生命周期。
在这里插入图片描述
electron包含两种进程:主进程和渲染进程。每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。主进程的主要目的是使用 BrowserWindow 模块创建和管理应用程序窗口。BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。 您可从主进程用 window 的 webContent 对象与网页内容进行交互。

const { BrowserWindow, app } = require("electron");

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 800,
  });

  win.loadURL("https://github.com"); //网页以地址的形式引入

  console.log(win.webContents); //主进程中输出
};

app.whenReady().then(() => {
  createWindow();
});

在这里插入图片描述
当一个 BrowserWindow 实例被销毁时,与其相应的渲染器进程也会被终止。

渲染进程:每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 洽如其名,渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页标准的 (至少就目前使用的 Chromium 而言是如此) 。渲染器无权直接访问 require 或其他 Node.js API

Preload 脚本:预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。预加载脚本可以在 BrowserWindow 构造方法中的 webPreferences .preload选项里被附加到主进程。预加载脚本与浏览器共享同一个全局 Window 接口,并且可以访问 Node.js API,所以它通过在全局 window 中暴露任意 API 来增强渲染器,以便你的网页内容使用。

虽然预加载脚本与其所附着的渲染器在共享着一个全局 window 对象,但您并不能从中直接附加任何变动到 window 之上,因为 contextIsolation 是默认的,因此下面这段代码在窗口的控制台中输出undefined

//preload.js
window.myAPI = {
  desktop: true
}
//renderer.js
console.log(window.myAPI)
// => undefined

因此需要使用 contextBridge 模块来安全地实现交互.

const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
  desktop: true
})

在这里插入图片描述

上下文隔离

上下文隔离功能将确保您的 预加载脚本 和 Electron的内部逻辑 运行在所加载的 webcontent网页 之外的另一个独立的上下文环境里。 这对安全性很重要,因为它有助于阻止网站访问 Electron 的内部组件 和 您的预加载脚本可访问的高等级权限的API

这意味着,实际上,您的预加载脚本访问的 window 对象并不是网站所能访问的对象。 例如,如果您在预加载脚本中设置 window.hello = ‘wave’ 并且启用了上下文隔离,当网站尝试访问window.hello对象时将返回 undefined。

自 Electron 12 以来,默认情况下已启用上下文隔离,并且它是 所有应用程序推荐的安全设置。

其实不使用contextBridge 创建预加载脚本就是没有开启上下文的方式。

进程通信

进程通信的表现形式例如 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。

IPC通道

在 Electron 中,进程使用 ipcMainipcRenderer模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。

  1. 渲染器进程到主进程(单向)
    要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收。如下是一个事例,用户输入input内容,点击按钮会修改窗口的标题
index.html
    Title: <input id="title"/>
    <button id="btn" type="button">Set</button>
    <script src="./renderer.js"></script>
renderer.js
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
  const title = titleInput.value
  window.aaa.setTitle(title) // 渲染主线程使用预加载脚本暴露的通道,使用其提供的方法
})
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("aaa", {
  //aaa是自定义的通道名
  //定义一个setTitle函数,通过send方法向主进程发生一个set-title消息,并携带参数
  setTitle: (title) => ipcRenderer.send("set-title", title), //send方法是一个异步行为
});
main.js
const { BrowserWindow, app, ipcMain } = require("electron");
const path = require("path");

function handleSetTitle(e, title) {
  /* 
  事件对象 e 中获取发送该事件的 webContents 对象。
  在 Electron 中,webContents 代表了渲染器进程内的网页内容,每个 Electron 窗口里都有一个与其关联的 webContents 实例
  */
  const webContents = e.sender;
  /* 
    BrowserWindow.fromWebContents 方法,并传入前面获得的 webContents 对象,
    可以获取到关联该 webContents 的 BrowserWindow 实例。
    基本上这行代码是根据渲染器进程(webContents)找到对应的窗口对象(BrowserWindow 实例)。
  */
  const w = BrowserWindow.fromWebContents(webContents);
  w.setTitle(title);
}

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 800,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
    webContents: {
      openDevTools: true,
    },
  });

  win.loadFile("index.html");
};

app.whenReady().then(() => {
  //ipcMain.on监听set-title行为,箭头从渲染器进程中使用setTitle方法发送而来的set-title消息(可以理解vue的事件监听emit)
  ipcMain.on("set-title", handleSetTitle);
  createWindow();
});

在这里插入图片描述
其中事件对象的结构如下
在这里插入图片描述

  1. 渲染器进程到主进程(双向):如下是一个事例,用户选中一个文件,显示该文件的路径。
index.html
    <button type="button" id="btn">Open a File</button>
    File path: <strong id="filePath"></strong>
    <script src="./renderer.js"></script>
renderer.js
const btn = document.getElementById("btn");
const filePathElement = document.getElementById("filePath");

btn.addEventListener("click", async () => {
  const filePath = await window.electronAPI.openFile();
  filePathElement.innerText = filePath;
});
preload.js
const { contextBridge, ipcRenderer } = require("electron/renderer");
contextBridge.exposeInMainWorld("electronAPI", {
  /* 
    异步发送一个需要响应的消息,并且等待主进程的回复。
    这是异步操作,但允许以类似于同步调用的方式(使用 async/await 或者基于 Promise 的方法)等待响应。
  */
  openFile: () => ipcRenderer.invoke("dialog:openFile"),
});
const { app, BrowserWindow, ipcMain, dialog } = require("electron/main");
const path = require("node:path");

async function handleFileOpen() {
  /* 
    dialog 模块用于显示原生的对话框,例如文件打开、保存、消息提示等。
    dialog 模块是 Electron 提供的一个系统对话框接口,可以在主进程中使用,用于与用户进行交互。
    dialog 模块通过其 showOpenDialog 方法用于显示一个文件选择对话框。
    这个方法是异步的,它会返回一个 Promise。当用户选择文件并关闭对话框时,Promise 被解决(resolved),并带有一个对象,这个对象包含了两个属性:canceled 和 filePaths。
    canceled 是一个布尔值,表示用户是否取消了对话框而没有选择任何文件
    filePaths 是一个数组,包含用户选择的一个或多个文件的完整路径。
  */
  const { canceled, filePaths } = await dialog.showOpenDialog();
  if (!canceled) {
    return filePaths[0];
  }
}

function createWindow() {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });
  mainWindow.loadFile("index.html");
}

app.whenReady().then(() => {
   //会把处理完成的消息返回给渲染进程
  ipcMain.handle("dialog:openFile", handleFileOpen);
  createWindow();
});
  1. 主进程到渲染器进程:将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 ·WebContents 实例·发送到渲染器进程。 此 WebContents 实例包含一个 ·send ·方法,其使用方式与· ipcRenderer.send ·相同。如下实例是一个原生操作系统菜单控制的数字计数器。
index.html
    Current value: <strong id="counter">0</strong>
    <script src="./renderer.js"></script>
renderer.js
const counter = document.getElementById("counter");

window.electronAPI.onUpdateCounter((value) => {
  const oldValue = Number(counter.innerText);
  const newValue = oldValue + value;
  counter.innerText = newValue.toString();
  window.electronAPI.counterValue(newValue);
});
preload.js
const { contextBridge, ipcRenderer } = require("electron/renderer");
contextBridge.exposeInMainWorld("electronAPI", {
  onUpdateCounter: (callback) =>
    ipcRenderer.on("update-counter", (_event, value) => callback(value)),
  counterValue: (value) => ipcRenderer.send("counter-value", value),
});
main.js
const { app, BrowserWindow, Menu, ipcMain } = require("electron/main");
const path = require("node:path");

function createWindow() {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });

  const menu = Menu.buildFromTemplate([
    {
      label: app.name,
      submenu: [
        {
          click: () => mainWindow.webContents.send("update-counter", 1),
          label: "Increment",
        },
        {
          click: () => mainWindow.webContents.send("update-counter", -1),
          label: "Decrement",
        },
      ],
    },
  ]);

  Menu.setApplicationMenu(menu);
  mainWindow.loadFile("index.html");

  // Open the DevTools.
  mainWindow.webContents.openDevTools();
}

app.whenReady().then(() => {
  ipcMain.on("counter-value", (_event, value) => {
    console.log(value); // 主进程中输出打印
  });
  createWindow();
});

在这里插入图片描述

  1. 渲染器进程到渲染器进程:没有直接的方法可以使用 ipcMain 和 ipcRenderer 模块在 Electron 中的渲染器进程之间发送消息。将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。从主进程将一个 MessagePort 传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。

进程沙盒化

从 Electron 20 开始,渲染进程默认启用了沙盒,无需进一步配置。

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

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

相关文章

政务云参考技术架构

行业优势 总体架构 政务云平台技术框架图&#xff0c;由机房环境、基础设施层、支撑软件层及业务应用层组成&#xff0c;在运维、安全和运营体系的保障下&#xff0c;为政务云使用单位提供统一服务支撑。 功能架构 标准双区隔离 参照国家电子政务规范&#xff0c;打造符合标准的…

TensorFlow图像识别项目

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 目录 今天我们将讨论如何部署Flask项…

星网络 StarNet: Rewrite the Stars

论文题目&#xff1a;Rewrite the Stars 论文链接&#xff1a;http://arxiv.org/abs/2403.19967 代码链接&#xff1a;https://github.com/ma-xu/Rewrite-the-Stars 一、摘要 揭示星操作将输入映射到高维、非线性特征空间的能力——类似于核技巧——而无需扩展网络。提出了S…

第7章 用户输入和 while 循环

第7章 用户输入和 while 循环 7.1 函数 input()的工作原理7.1.1 编写清晰的程序7.1.2 使用 int()来获取数值输入7.1.3 求模运算符 7.2 while 循环简介7.2.1 使用 while 循环7.2.2 让用户选择何时退出7.2.3 使用标志7.2.4 使用 break 退出循环7.2.5 在循环中使用 continue7.2.6 …

Django DetailView视图

Django的DetailView是一个用于显示单个对象详情的视图。下面是一个使用DetailView来显示单个书籍详情的例子。 1&#xff0c;添加视图 Test/app3/views.py from django.shortcuts import render# Create your views here. from django.views.generic import ListView from .m…

deepin V23 新进展!deepin 成功适配 MilkV Mars

内容来源&#xff1a;deepin&#xff08;深度&#xff09;社区 deepin-ports SIG&#xff08;Special Interest Group&#xff0c;特别兴趣小组&#xff09;&#xff0c;deepin&#xff08;深度&#xff09;社区积极与各大厂商展开合作&#xff0c;参与对 RISC-V 架构和设备的适…

鸿蒙原生开发——轻内核A核源码分析系列三 物理内存(2)

3.1.2.3 函数OsVmPhysLargeAlloc 当执行到这个函数时&#xff0c;说明空闲链表上的单个内存页节点的大小已经不能满足要求&#xff0c;超过了第9个链表上的内存页节点的大小了。⑴处计算需要申请的内存大小。⑵从最大的链表上进行遍历每一个内存页节点。⑶根据每个内存页的开始…

10秒变鬼短视频:四川鑫悦里文化传媒有限公司

10秒变鬼短视频&#xff1a;创意与惊悚的完美融合 在短视频的世界里&#xff0c;创新与独特性 节奏、巧妙的剪辑和惊悚的氛围&#xff0c;成为了许多观众喜爱的对象。四川鑫悦里文化传媒有限公司将探讨“10秒变鬼”短视频的创作技巧、受众心理以及其对短视频行业的启示。 一…

人脸识别系统---多人脸提取

一、多人脸提取 1.导包、设置窗口、选择文件夹里图片的函数和单人脸提取一致&#xff0c;此处省略&#xff08;详情看‘单人脸提取’&#xff09; 2.对图像进行处理&#xff0c;在窗口画布上显示出来 2.1定义名为tq的函数&#xff0c;在函数内部声明了三个全局变量file_path…

[Spring Boot实战] 如何快速地创建spring boot项目

文章目录 一、背景说明二、环境说明三、创建步骤3.1 选择项目JDK版本和Initializer服务地址3.2 设置项目的元数据3.3 引入依赖3.4 选择项目路径 四、额外的工作五、问题5.1 为什么使用Lombok&#xff1f; 一、背景说明 使用spring boot创建JAVA项目&#xff0c;是目前比较主流…

数据结构与算法笔记:基础篇 - 红黑树(下):掌握这些技巧,你也可以实现一个红黑树

概述 红黑树是一个让人又爱又恨的数据结构&#xff0c;“爱” 是因为它稳定、高性能&#xff0c;“恨” 是因为实现起来实在太难了。本章讲红黑树的实现&#xff0c;对于基础不太好的同学&#xff0c;理解起来可能会有些困难。但是&#xff0c;我觉得没有必要去死磕它。 为什…

一个公用的数据状态修改组件

灵感来自于一项重复的工作&#xff0c;下图中&#xff0c;这类禁用启用、审核通过不通过、设计成是什么状态否什么状态的场景很多。每一个都需要单独提供接口。重复工作还蛮大的。于是&#xff0c;基于该组件类捕获组件跳转写了这款通用接口。省时省力。 代码如下&#xff1a;…

专业学习|博弈论-课程沿革

学习来源&#xff1a;北京大学刘霖《博弈论》MOOC公开课 备注&#xff1a;仅做学习分享&#xff0c;请勿转载&#xff0c;转载必究&#xff01; &#xff08;一&#xff09;博弈论的预备知识 基本的微积分的知识和概率论的知识。简单的说会求导数&#xff0c;会求简单的积分&am…

文案策划日常大公开:掌握这些技巧,让你工作效率翻倍

本周的工作和生活安排得满满当当&#xff0c;但这些看似繁重的任务&#xff0c;其实都是我利用碎片化时间高效完成的。 这里和大家分享一下&#xff0c;我是如何在这忙碌的一周中&#xff0c;既完成本职工作又享受生活的。 本周工作日常汇总&#xff1a; 1. 品牌小红薯笔记&…

Java高阶数据结构-----并查集(详解)

目录 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; &#x1f92a;二.并查集代码&#xff1a; &#x1f602;三&#xff1a;并查集的一些习题&#xff1a; A.省份数量 B.等式方程的可满足性 &#x1f9d0;一.并查集的基本概念&实例&#xff1a; 并查集概念&…

5.3 数据模型设计总结

概述&#xff1a; 数据模型设计是指根据需求和目标设计出合适的数据模型的过程。数据模型是对现实世界中数据的抽象和表示&#xff0c;它定义了数据的结构、关系和约束。数据模型设计的目标是保证数据的一致性、完整性和可用性&#xff0c;同时满足系统性能和可扩展性的要求。…

Python基础教程(十二):模块

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

【Java】解决Java报错:IOException during File Operations

文章目录 引言一、IOException的定义与概述1. 什么是IOException&#xff1f;2. IOException的常见触发场景3. 示例代码 二、解决方案1. 检查文件是否存在2. 使用try-with-resources语句3. 捕获和处理IOException4. 使用NIO进行文件操作 三、最佳实践1. 检查文件状态2. 使用try…

为什么你应该本地化你的软件

本地化您的软件是一项战略举措&#xff0c;可以显著提高其成功率和影响力。以下是您应该投资于软件本地化的几个令人信服的原因&#xff1a; 扩大您的市场覆盖范围 通过本地化您的软件&#xff0c;您可以开拓新市场并接触到更广泛的受众。许多用户更喜欢甚至需要他们母语的软…

(十二)人工智能应用--深度学习原理与实战--模型编译及训练参数的选择

神经网络模型的编译实际上是为网络指定几个非常重要的运行参数,包括优化器、损失函数(误差函数】和评价指标,这三者也代表着神经网络的核心运行机制----通过损失函数来计算网络误差、通过优化器来调整网络参数以降低误差、通过评价指标来衡量网络的性能。神经网络训练时除了…