【Electron入门】进程环境和隔离

news/2025/2/26 9:36:58

目录

一、主进程和渲染进程

1、主进程(main)

2、渲染进程(renderer)

二、预加载脚本

三、沙盒化

为单个进程禁用沙盒

全局启用沙盒

四、环境访问权限控制:contextIsolation和nodeIntegration

1、contextIsolation

🌰启用contextIsolation:true时(默认推荐做法)

🌰不启用contextIsolation:false时

2、nodeIntegration

🌰 如果启用nodeIntegration: true(不推荐)

🌰如果不启用,我们应该如何借助Node.js的功能呢?


Electron可以简单理解为一个桌面应用程序的“壳”,内里还是遵循浏览器的行为,加载网页进行渲染(可以是本地、也可以是远程网页

一、主进程和渲染进程

Electron的架构其实类似现代浏览器,为了管理应用程序窗口中不同的页面,每个标签页在自己的进程中渲染, 从而限制了一个网页上的有误或恶意代码可能导致的对整个应用程序造成的伤害。

在Electron中,我们可控制的两类进程为:主进程渲染进程

1、主进程(main)

1️⃣ 运行环境:node.js,具有 require 模块和使用所有 Node.js API

2️⃣ 职责:

  • 窗口管理:创建 / 销毁窗口实例BrowserWindow,其相当于一个小的EventEmitter,窗口将在单独的渲染器进程中加载一个网页。窗口中的webContents 代表的是 渲染进程 内部的网页内容(Web 页面),可以执行向渲染进程通信、网页内容生命周期监听、访问/控制devtools
javascript">// main.js
const { BrowserWindow } = require('electron')

const win = new BrowserWindow({ width: 800, height: 1500 })
win.loadURL('https://github.com')

const contents = win.webContents
console.log(contents)
  • 应用程序的生命周期:监听app生命周期、做相应处理
javascript">// quitting the app when no windows are open on non-macOS platforms
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})
  • 调用api与操作系统交互:Electron 有着多种控制原生桌面功能的模块,例如菜单、对话框以及托盘图标。下面举个例子🌰,创建应用菜单
javascript">const { app, Menu, BrowserWindow } = require('electron');

app.whenReady().then(() => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });

  win.loadURL('https://example.com');

  // 创建菜单template
  const menuTemplate = [
    {
      label: '文件',
      submenu: [
        { label: '打开文件', click: () => console.log('打开文件') },
        { type: 'separator' }, // 分割线
        { label: '退出', role: 'quit' }
      ]
    },
    {
      label: '编辑',
      submenu: [
        { label: '撤销', role: 'undo' },
        { label: '重做', role: 'redo' },
        { type: 'separator' },
        { label: '剪切', role: 'cut' },
        { label: '复制', role: 'copy' },
        { label: '粘贴', role: 'paste' }
      ]
    }
  ];

  // 设置应用菜单
  const menu = Menu.buildFromTemplate(menuTemplate); //从模版设置菜单实例
  Menu.setApplicationMenu(menu);  // 放入应用菜单
});

2、渲染进程(renderer)

1️⃣ 运行环境:渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页web标准的。 

二、预加载脚本

由于主进程和渲染进程的运行环境完全不同,且默认情况下,二者无权直接访问互相之间的环境。

所以出现了预加载脚本preload.js作为环境桥梁,它包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码,与浏览器共享一个全局window接口 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。

preload.js可以部分暴露一些api给对方进程、可以作为通信中转站等等。

三、沙盒化

当 Electron 中的渲染进程被沙盒化时,它们的行为与常规 Chrome 渲染器一样。 一个沙盒化的渲染器不会有一个 Node.js 环境。

附属于沙盒化的渲染进程的 preload 脚本中仍可使用一部分以 Polyfill 形式实现的 Node.js API。

为单个进程禁用沙盒

javascript">app.whenReady().then(() => {
  const win = new BrowserWindow({
    webPreferences: {
      sandbox: false
    }
  })
  win.loadURL('https://google.com')
})

全局启用沙盒

app.enableSandbox :注意,此 API 必须在应用的 ready 事件之前调用

javascript">app.enableSandbox()
app.whenReady().then(() => }
  // 因为调用了app.enableSandbox(),所以任何sandbox:false的调用都会被覆盖。
  const win = new BrowserWindow()
  win.loadURL('https://google.com')
})

四、环境访问权限控制:contextIsolation和nodeIntegration

在创建一个browserWindow实例的时候,会配置webPreferences中的字段。

其中有两个字段与渲染进程的访问权限密切相关:contextIsolation 和 nodeIntegration

1、contextIsolation
  • 控制 window 对象是否在独立的 JavaScript 上下文中运行。
  • 默认值:true
  • 如果为true,代表渲染进程在独立的js上下文中。因此preload.js、第三方库都不能直接修改渲染进程中的window全局对象;如果为false,preload.js可以直接通过修改window属性传递值

那么不同设置下,如何借助预加载脚本让主进程与渲染进程通信呢?

🌰启用contextIsolation:true时(默认推荐做法)

main.js

javascript">const { BrowserWindow } = require('electron');
const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,  // 开启上下文隔离
    preload: 'preload.js'
  }
});

preload.js:利用exposeInMainWorld将属性挂在window对象中

javascript">const { contextBridge } = require('electron');

contextBridge.exposeInMainWorld('myAPI', {
  sayHello: () => console.log('Hello!')
});

renderer.js

javascript">console.log(window.myAPI.sayHello()); // ✅ "Hello!"
🌰不启用contextIsolation:false时

preload.js:可直接篡改window对象

javascript">window.myApi= {
   sayHello: () => console.log('Hello!')
};

2、nodeIntegration
  • 控制是否可以在渲染进程中直接使用Node.js API(如fs、path、require等语法和api)
  • 默认值:false,无法直接使用node环境
🌰 如果启用nodeIntegration: true(不推荐)

渲染进程中的js文件

javascript">// 渲染进程 index.html
<script>
  const fs = require('fs');
  fs.writeFileSync('test.txt', 'Hello, Electron!');
</script>
🌰如果不启用,我们应该如何借助Node.js的功能呢?

通过【 预加载脚本】执行ipcRenderer通信,发送至主进程处理

 preload.js

javascript">// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  sendTitle: (title) => ipcRenderer.send('set-title', title),
  readFile : (filePath) => ipcRenderer.invoke('read-file', filePath)
});

main.js的利用ipcMain接受处理函数

javascript">const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')
const fs = require('fs').promises;

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

  // send 'sendTitle'方法的接收器
  ipcMain.on('set-title', (event, title) => {
    //通过event.sender获取网页内容对象
    const webContents = event.sender
    // fromWebContents解析
    const win = BrowserWindow.fromWebContents(webContents)
    win.setTitle(title)
  })

  // invoke 'readFile'方法的处理器
  ipcMain.handle('read-file', async (_, filePath) => {
      try {
        const data = await fs.readFile(filePath, 'utf-8');
        return { success: true, data };
      } catch (error) {
        return { success: false, error: error.message };
      }
  });

  mainWindow.loadFile('index.html')
}

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

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

⚠️注意:send + on和invoke + handle两种通信方法的区别

 send + on单向通信,单向发送->单边处理

invoke + handle双向通信,发送者发送信息 -> 接收者理后可return一个返回值 -> 发送者接收到返回值的Promise对象,可针对返回值再处理

🌟推荐实践:

启用 sandbox: true(沙箱模式)进一步增强安全性

javascript">const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true, (默认,无需特别设置)
    nodeIntegration: false,(默认,无需特别设置)
    preload: 'preload.js',
    sandbox: true
  }
});

http://www.niftyadmin.cn/n/5868486.html

相关文章

LeetCode 热题100 141. 环形链表

LeetCode 热题100 | 141. 环形链表 大家好&#xff0c;今天我们来解决一道经典的算法题——环形链表。这道题在 LeetCode 上被标记为简单难度&#xff0c;要求我们判断一个链表中是否存在环。下面我将详细讲解解题思路&#xff0c;并附上 Python 代码实现。 题目描述 给定一个…

DeepSeek开源周Day2:DeepEP - 专为 MoE 模型设计的超高效 GPU 通信库

项目地址&#xff1a;https://github.com/deepseek-ai/DeepEP 开源日历&#xff1a;2025-02-24起 每日9AM(北京时间)更新&#xff0c;持续五天 (2/5)&#xff01; ​ ​ 引言 在大模型训练中&#xff0c;混合专家模型&#xff08;Mixture-of-Experts, MoE&#xff09;因其动…

锂电池使用和存储电压

表格补充说明&#xff1a; 每列数据中&#xff0c;2S和3S电池的数值都是单电芯数值的2倍和3倍&#xff1b;对于其他电压的电池&#xff0c;将单电芯数值乘以相应S数即可&#xff1b;理论上单个电芯过放电压为3.0V&#xff0c;实际中为了保险&#xff0c;电压降到3.6V即需充电。…

深度学习笔记线性代数方面,记录一些每日学习到的知识

记录一些每日学习到的新知识&#xff1a; torch&#xff1a;Torch是一个有大量机器学习算法支持的科学计算框架&#xff0c;是一个与Numpy类似的张量(Tensor)操作库 jupyter&#xff1a;Jupyter Notebook 的本质是一个 Web 应用程序&#xff0c;便于创建和共享程序文档&#…

centos系统MBR格式转换成gpt格式 (华为云)

在华为云上的centos7.9系统MBR格式转换成GPT格式的步骤 华为云上关于转换的步骤 这个链接里面 gdisk -g /dev/vda 是不对的&#xff0c;-g参数是新创建一个分区&#xff0c;慎用 自己步骤如下&#xff1a;&#xff08;已经试验过&#xff09; 1、gdisk /dev/sda (这里是盘 不…

请谈谈 React 中的状态管理,如何使用 Context API 和 Redux 进行状态管理?

一、Context API 深度应用 1. 核心实现原理 通过createContext创建上下文对象&#xff0c;使用Provider组件包裹需要共享状态的组件树&#xff0c;子组件通过useContext Hook或Consumer组件消费数据。 代码示例&#xff08;主题切换场景&#xff09;&#xff1a; // 创建上…

反制无人机详细全面介绍

一、反制系统的核心架构 侦测识别层 采用雷达、光电/红外传感器、无线电频谱监测等技术实现全空域覆盖。毫米波雷达可探测微型无人机&#xff0c;声学探测适用于低噪声环境4&#xff0c;而被动射频定位技术可追踪2-8公里范围内的目标。多传感器融合技术&#xff08;如雷达光电A…

React(10)

项目实践--创建项目 在store的modules中创建相关的子仓库暴露到仓库index文件中 导入creatSlice和axios 创建仓库 和数据的异步修改方法 // 编写store // 导入createSlice和axios import { createSlice } from "reduxjs/toolkit"; import axios from "axios&…