一、主进程(main.ts)
主进程在 Node.js 环境中运行,在主进程中可以使用 require 模块和所有 Node.js API
main/main.ts
import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import MenuBuilder from './menu';
let mainWindow: BrowserWindow | null = null;
//进程间通信
ipcMain.on('app.quit', () => {
app.quit()
});
//创建窗口
const createWindow = async () => {
mainWindow = new BrowserWindow({
frame: true, //有边框窗口
width: 1280,
height: 720,
webPreferences: {
//指定预加载脚本的路径
preload: path.join(__dirname, 'preload.js'),
webSecurity: false,
//在渲染进程中使用node的API时,需要设为true
// nodeIntegration: true,
//关闭语境隔离
// contextIsolation: false,
},
});
mainWindow.loadURL('https://github.com');
//渲染进程第一次完成绘制时,如果窗口还没有被显示,触发该事件
mainWindow.on('ready-to-show', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
if (process.env.START_MINIMIZED) {
mainWindow.minimize();
} else {
mainWindow.show();
mainWindow.focus();
}
});
//窗口关闭时触发
mainWindow.on('closed', () => {
mainWindow = null;
});
//自定义菜单
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
//进程间通信
mainWindow.webContents.on('new-window', (event, url) => {
//阻止默认的跳链接行为
event.preventDefault();
//在用户的默认浏览器中打开url
shell.openExternal(url);
});
};
//所有窗口关闭时触发
app.on('window-all-closed', () => {
//用户不在 macOS ( darwin) 上,就触发app.quit()
if (process.platform !== 'darwin') {
app.quit();
}
});
//browser window 只能在app的ready事件(初始化)被触发后创建
app
.whenReady()
.then(() => {
createWindow();
//应用被激活时触发
app.on('activate', () => {
//没有窗口时创建窗口
if (mainWindow === null) createWindow();
});
})
.catch(console.log);
二、预加载(preload.js)
预加载脚本包含了要在渲染进程中执行,但比网页内容先开始加载的代码。
- 在主进程中,createWindow时,需要通过
webPreferences指定preload.js的路径 - 预加载脚本与浏览器(渲染器)共享同一个全局
window对象,并且可以访问Node的API - 语境隔离(Context Isolation):默认开启,意味着预加载脚本与渲染器的主要运行环境是隔离开来的,以避免 预加载脚本可访问的高级权限API 被泄漏到网页内容代码中。
- 在预加载脚本中,通过
contextBridge将要使用的node API暴露给全局window对象。进而在渲染进程中使用这些node API。
三、渲染进程(页面.js)
默认情况下,在渲染进程中无法直接使用node的API。
如果要在渲染进程中使用node的API(比如使用fs、require):
- 方法一:在主进程createWindow时,配置
nodeIntegration: true后,渲染进程可以使用node API。页面.js
let fs = require('fs')
let btn = document.querySelector('button')
let i = 0;
btn.onclick = function(){
i++;
fs.writeFile(`fileName${i}.txt`,'点击按钮后写入的内容',(err) => {
if(err){
console.log('err:',err)
}else{
console.log('写入完毕')
}
})
}
-
方法二:通过preload.js
在
contextIsolation默认为true的情况下:需要使用contextBridge
- 在
preload.js中 通过contextBridge将要使用的node API暴露给全局window对象
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('apiName', {
desktop: true
})
- 在渲染进程
render.js中通过window对象调对应node API来使用
console.log(window.apiName) // { desktop: true }
举例如下:
main/preload.js
const { contextBridge } = require('electron');
const uploadCameraImage = require('./uploadCameraImage').uploadCameraImage;
contextBridge.exposeInMainWorld('uploadCameraImage', uploadCameraImage);
页面.js
function formatImage(path) {
//因为window为全局对象,此时无需导入uploadCameraImage
let imageData = window.uploadCameraImage(path);
if (imageData) {
...
}
}
main/uploadCameraImage.js
const fs = require('fs');
const mineType = require('mime-types');
function uploadCameraImage(newpath) {
//let imageData = fs.readFileSync(cameraImagePath);
let imageData = fs.readFileSync(newpath);
let base64 = imageData.toString('base64');
let base64format = 'data:' + mineType.lookup(newpath) + ';base64,' + base64;
let blobdata = convertBase64UrlToBlob(base64format);
// const blobnewurl = window.URL.createObjectURL(blobdata)
return blobdata;
}
module.exports = {
uploadCameraImage,
...
};
在contextIsolation设置为flase的情况下:无需使用contextBridge
preload.js
// 上下文隔离禁用的情况下使用预加载
window.myAPI = {
doAThing: () => {}
}
页面.js
// 在渲染器进程使用导出的 API
window.myAPI.doAThing()