Electron-Vite快速识别

构建Vite-electron项目

npm i electron-vite -D

npm create @quick-start/electron project-name -- template vue-ts

Electron的运行流程

Electron 运行流程

Electron进程

一、主进程:有且唯一的应用程序入口点,在 Node.js 环境中运行,具有 require 模块和使用所有 Node.js API 的能力。
二、渲染进程:每个打开的 BrowserWindow ( 与每个网页嵌入 )都会生成一个单独的渲染器进程,无权直接访问 require 或其他 Node.js API,入口为HTML文件
三、预加载脚本:包含执行于渲染器进程中,且先于网页内容开始加载的代码 。 运行在渲染进程的环境中,却有使用Node.js API的能力,在BrowserWindow 构造方法中的 webPreferences 选项里被附加到主进程。
ps:由于预加载脚本与渲染进程的运行环境是隔离开,如果想要在预加载脚本中暴露信息则使用``contextBridge`模块实现,但也有暴露限制例如暴露原型或者 Symbol。不可直接暴露权限API,可采取暴露API功能实现方法。

Electron的模块

app模块:控制应用程序的生命周期
BrowserWindow模块:窗口管理,每个实例对应一个窗口,且在单独的渲染器进程中加载一个网页。当实例被销毁时,与其相应的渲染器进程也会被终止
autoUpdater模块:程序自动更新,window和mac支持
BrowserView模块:创建和控制视图,相对于父窗口的子窗口,窗口里面开窗口
clipboard模块:在系统剪贴板上执行复制和粘贴操作
dialog模块:显示用于打开和保存文件、警报等的本机系统对话框
powerMonitor模块:监听电源状态变化
powerSaveBlocker模块:阻止系统进入低功耗 (休眠) 模式
screen模块:检索有关屏幕大小、显示器、光标位置等的信息
safeStorage 模块:允许访问简单的加密和解密字符串,以便存储在本地机器上
session模块:管理浏览器会话、cookie、缓存、代理设置等
protocol模块:注册自定义协议并拦截基于现有协议的请求
pushNotifications模块:注册并接收远程推送通知服务的通知
shell模块:使网页在本地浏览器打开而不是在窗口中,新窗口直接window.open(url)即可
systemPreferences模块:获取系统偏好
Tray模块:添加图标和上下文菜单到系统通知区
globalShortcut模块:全局快捷键

const globalShortcut = electron.globalShortcut; //引入
//const globalShortcut = require('global-shortcut');

//!必须在ready监听回调中使用
app.on("ready", () => {
globalShortcut.register("ctrl+x", () => {
//使用快捷键的回调
});
let judgeRegister = globalShortcut.isRegister('ctrl+e')?'注册成功':'注册失败' //判断快捷键是否注册成功
});
app.on('will-quit', ()=> {
globalShortcut.unregister('ctrl+x'); //注销ctrl+x快捷键
globalShortcut.unregisterAll(); //注销全部快捷键
});

contextBridge模块:在隔离的上下文中创建一个安全的、双向的、同步的桥梁
nativeImage模块:使用 PNG 或 JPG 文件创建托盘、dock和应用程序图标
webFrame模块:自定义渲染当前网页
ipc模块:进程通信,负责进程之间的事件触发,数据传递
1、渲染进程单向通信主进程 // 修改程序名称
// index.html
<html>
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
  </head>
  <body>
    Title: <input id="title"/>
    <button id="btn" type="button">Set</button>
    <script src="./renderer.js"></script>
  </body>
</html>
// main.ts
const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')

function handleSetTitle (event, title) {
    // event是消息发送方,
  const webContents = event.sender
   // 确定是哪个渲染进程调用,然后作用与该渲染进程
  const win = BrowserWindow.fromWebContents(webContents)
  win.setTitle(title)
}
function createWindow () {
  const mainWindow = new BrowserWindow({
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })
  mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
  ipcMain.on('set-title', handleSetTitle)
  createWindow()
}
// preload.ts
const { contextBridge, ipcRenderer } = require('electron')
// 通过contextBridge安全的向渲染进程暴露一个electronAPI对象,其中是渲染进程所需实现业务的方法,不可直接暴露Api给渲染
contextBridge.exposeInMainWorld('electronAPI', {
    setTitle: (title) => ipcRenderer.send('set-title', title)
    ... // 更多方法
})
// render.ts
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
    const title = titleInput.value
       // 点击触发proload提供的winndow.electronAPI对象中的setTitle方法
    window.electronAPI.setTitle(title)
});
2、主进程渲染进程双向通信 // 渲染进程调用主进程的原生文件对话框
// index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Dialog</title>
  </head>
  <body>
    <button type="button" id="btn">Open a File</button>
    File path: <strong id="filePath"></strong>
    <script src='./renderer.js'></script>
  </body>
</html>
// render.ts
const btn = document.getElementById('btn')
const filePathElement = document.getElementById('filePath')
btn.addEventListener('click', async () => {
   // 触发由contentBrige模块暴露的window.electronAPI对象openFile方法
  const filePath = await window.electronAPI.openFile()
  filePathElement.innerText = filePath
})
// preload.ts
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI',{
       // 双向通信触发通过invoke方法
  openFile: () => ipcRenderer.invoke('dialog:openFile')
})
// main.ts
const {app, BrowserWindow, ipcMain, dialog} = require('electron')
const path = require('path')

async function handleFileOpen() {
  const { canceled, filePaths } = await dialog.showOpenDialog()
  if (canceled) {
    return
  } else {
    return filePaths[0]
  }
}

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

app.whenReady().then(() => {
 // 双向通信接收通过handle方法
  ipcMain.handle('dialog:openFile', handleFileOpen)
  createWindow()
  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})
3、主进程通信渲染进程
// index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Menu Counter</title>
  </head>
  <body>
    Current value: <strong id="counter">0</strong>
    <script src="./renderer.js"></script>
  </body>
</html>
// render.ts
const counter = document.getElementById('counter')

window.electronAPI.handleCounter((event, value) => {
    const oldValue = Number(counter.innerText)
    const newValue = oldValue + value
    counter.innerText = newValue
       // 回复??
    event.sender.send('counter-value', newValue)
})
// preload.ts
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
  handleCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
// main.ts
const {app, BrowserWindow, Menu, ipcMain} = require('electron')
const path = require('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')
}
 app.whenReady(()=>{
    // 接收渲染进程回复的
    ipcMain.on('counter-value', (_event, value) => {
       console.log(value) 
   })
})
//...


4、渲染进程通信渲染进程
一般很少用,因为渲染进程用框架例如Vue有N种通信方式
1、把主进程当作事件总线来用
2、主进程将一个MessagePort传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。

设置网络是否连接的不同回调

 window.addEventListener("online", () => {});   //上网
 window.addEventListener("offline", () => {});    //断网
posted @ 2022-12-21 17:48  kq1024  阅读(736)  评论(0)    收藏  举报