3. 如何在electron中使用node

2,803 阅读3分钟

如何在electron中安全的使用nodejs

在简单的接触了electron了之后,大家可能都很疑惑,这不就是一个套壳吗,electron可不只能做到这些,如果要开发一个桌面应用端, 不可避免的需要一些IO操作等, 仅依赖JavaScript是远远不够的, electron 是基于 chromiumNode.js 构建应用的, 那么我们需要怎么去操作Node

webPreferences

BrowserWindow 中, 我们能传递一个配置参数 webPreferences,它的可配置项:

  • nodeIntegration:是否集成node.js,默认为false
  • contextIsolation:上下文隔离,默认为true
  • preload:指定预加载的js文件绝对路径

看起来我们似乎只要把nodeIntegration,contextIsolation配置为truefalse即可,但是这是非常不安全的一种行为, 在electron中,它更推荐我们使用preload中操作 node,我们在项目根目录下继续创建一个preload.js 通过修改main.js, 把之前使用的生命周期试验给删除掉,仅保留必须的平台差异化处理的生命周期, 接着配置preload,这里需要注意的是,preload值为一个绝对路径,我们需要使用node中的path去处理,此时我们的 main.js 如下:

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

const createWindows = () => {
  const win = new BrowserWindow({
    width: 1280,
    height: 800,
    webPreferences: {
      nodeIntegration: true,
      preload: path.join(__dirname, 'preLoad.js'),
    },
  })

  win.loadFile('index.html')

  win.webContents.openDevTools()
}

app.whenReady().then(() => {
  createWindows()
  // 仅 macOS 支持
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindows()
    }
  })
})

app.on('window-all-closed', () => {
  // 对于 Mac 系统, 关闭窗口时并不会直接退出应用, 此时需要我们来手动处理
  if (process.platform === 'darwin') {
    console.log('close')
    app.quit()
  }
})

这时我们的nodejs代码就可以写在 preload.js中了。简单试验一下,在preLoad.js中输入如下代码

// proLoad.js
console.log(process.platform)

此时打开控制台就能读取到自己电脑的操作系统了,不过仅仅在preLoad.js文件中使用node对开发者而言还是不够的

contextBridge

electron提供了一个apicontextBridgecontextBridgeElectron 中用于在主进程和渲染进程之间建立安全的通信通道,它提供的主要 API 有:

  1. contextBridge.exposeInMainWorld(apiKey, api)

  • apiKey string - 将 API 挂载到 window 的键。 API 将可通过 window[apiKey] 访问。
  • api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。

contextBridge.exposeInIsolatedWorld(worldId, apiKey, api)

  • worldId Integer - 要注入 API 的 world 的 ID。 0 是默认 world,999 的 world 被 Electron 的 contextIsolation 使用。 使用 999 would 为 preload 上下文暴露对象。 我们建议使用 1000+ 来创建隔离的 world。
  • apiKey string - 将 API 注入到 window 的键。 API 将可通过 window[apiKey] 访问。
  • api any - 你的 API可以是什么样的以及它是如何工作的相关信息如下。

那么我们就知道了, 此时我们可以先试试在preLoad.js中通过exposeInMainWorld暴露一个api看看能否访问到

// preLoad.js
const { contextBridge } = require('electron')
const fs = require('path')

console.log(process.platform)
console.log(fs)

contextBridge.exposeInMainWorld('callNode', {
  platform: process.platform
})

新建一个renderer文件夹,新建一个app.js文件,index.html中引入这个app.js,我们是在在app.js中通过window访问callNode

/renderer/app.js

console.log(window.callNode)

electron通过window把我们定义的key-value挂载到window,使得我们可以通过 window这个全局对象去访问我们暴露的值

总体来说,contextBridge 是 Electron 多进程通信的未来方向,使用它可以构建更安全的应用。