electron

2023-12-26  本文已影响0人  何亮hook_8285

主进程和渲染进程

electron分为主进程渲染进程,以下是主进程和渲染进程区别

123.png

第一个Electron项目

1.初始化项目

#创建一个目录
mkdir my-electron-app && cd my-electron-ap
#初始化项目描述
npm init
#安装electron依赖
npm install electron --save-dev
#nodemon 可监控文件变化,自动运行electron命令
npm install nodemon --save-dev
#安装devtron,它可以通过可视化方式查看IPC信息,在js中使用 require('devtron').install()
npm install devtron --save-dev
#安装打包工具
npm install electron-builder --save-dev
#安装压缩工具,解压 asar extract app.asar
npm install -g asar 

2.初始化项目package.json配置信息

{
  "name": "my-electron-app",
  "version": "1.0.0",
  "description": "Hello World!",
  "main": "main.js",
  "scripts": {
    "start": "nodemon --watch main.js --exec \"electron .\"", //启动electron
    "build": "electron-builder"
  },
  "author": "Jane Doe",
  "license": "MIT",
  "devDependencies": {
    "electron": "23.1.3"
  }
}

3.创建一个main.js程序入口文件,内容如下代码

//引入electron模块,并且导入app对象和browserWindow对象
//app 对象负责应用程序的事件生命周期
//BrowserWindow 对象负责创建和管理应用窗口
const { app, BrowserWindow } = require('electron')


//创建窗口函数
const createWindow = () => {
  //创建浏览器窗口 
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })
 //加载本地界面
  win.loadFile('index.html')
  //打开开发者工具
  win.webContents.oepnDevTools() 
}
//应用启动时加载回调事件
app.whenReady().then(() => {
  //开启调试工具,监测IPC信息
  require('devtron').install()  
  //创建渲染窗口 
  createWindow()
})

//或者使用应用启动时加载回调事件
//app.on("ready",()=>{
//    createWindow()
//})

//关闭应用
app.quit()

4.创建入口文件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'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>my is Electron renderer!</title>
  </head>
  <body>
    <h1>my is Electron renderer!</h1>
    <p>ok</p>
  </body>
</html>

窗口对象

BrowserWindow对象代表窗口对象,主窗口对象只能存在一个。

//主窗口
const win = new BrowserWindow({
    width: 800, //窗口宽度
    height: 600, //窗口高度
    x:100, //窗口在屏幕x坐标位置
    y:100, //窗口在屏幕y坐标位置
    show:false, //不显示窗口
    maxHeight:1000, //最大高度
    maxWidth:1000, //最大宽度
    minHeight:200, //最小高度
    minWidth:300, //最小高度
    resizeable:false, //限制窗口不能缩放
    title:"我的electron", //设置窗口标题
    icon:'my.icon',//设置图标
    frame:false,//设置标题栏,false隐藏掉,true显示
    transparent:true,//开启透明窗体
    autoHideMenuBar:true,//是否显示菜单栏,true显示,false隐藏
    modal:false, //是否开启父子及模式窗口,如果开启模式窗口需要关闭子窗口才能操作主窗口
    webPreferences:{ //设置web窗口预选参数配置
        // 开启node模块
        nodeIntegration:true,
        //开启远程模块,让渲染窗口能使用electron Api函数
        enableRemoteModule:true,
        preload:path.join(__dirname,'preload.js'), //预加载js
        contextIsolation:false, //设置此项为false,才可在渲染进程中使用electron api
        webSecurity:false //去掉跨越验证
    }
  })
 //加载到渲染进程中
  win.loadFile('index.html')
  win.on('ready-to-show',()=>{
      //显示窗口
      win.show()
  })

//创建子创建
let secondWin = new BrowserWindow({
    width: 300, //窗口宽度
    height: 300, //窗口高度
    webPreferences:{ //设置web窗口预选参数配置
        // 开启node模块
        nodeIntegration:true,
        //开启远程模块,让渲染窗口能使用electron Api函数
        enableRemoteModule:true
    },
    parent:win //子窗口绑定到父窗口对象上
  })
//加载到渲染进程中
 secondWin.loadFile('second.html')

//----------------------------------------------------
//窗口对象中常用函数
const {remote} = require('electron')

//获取当前窗口句柄
let mainWin = remote.getCurrentWindow()
//关闭窗口
mainWin.close()
//获取窗口是否最大化
mainWin.isMaximized()
//设置窗口最大化
mainWin.maximize()
//将窗口还原
mainWin.restore()
//获取窗口是否最小化
mainWin.isMinimize()
//设置窗口最小化
mainWin.minimize()

//防止窗口关闭,关闭渲染窗口回调事件
window.onbeforeunload =()=>{
    return false;
}

//销毁窗口
mainWin.destroy()



//或者使用window.open打开页面
window.open('index.html','_self')

生命周期

8b6e5736ef77fabb44c0f19d3fbe336e.png
//----------------------------------应用的生命周期-------------------------------------
//应用加载加载前,可接受proctl传递参数
app.on('will-finish-launching',(event)=>{
  app.on('open-url', (event, url) => {
    log(`==> app-event: open-url <===`, url);
  });
})

//当【首次启动应用程序】、【在程序已经运行后再次打开程序】或【单击应用程序的坞站或任务栏图标时】重新激活它
app.on('did-become-active',()=>{
    
})

//应用加载完成通知
app.on('ready',()=>{
    
})

//当应用全部关闭时通知
app.on('window-all-closed',()=>{
    
})

app.on('before-quit', function (event) {
    // event.preventDefault() {2}
    console.log('before-quit')
})
app.on('will-quit', function (event) {
    // event.preventDefault()
    console.log('will-quit')
})

//当Windows 系统中,如果应用程序因系统关机/重启或用户注销而关闭,那么 before-quit和 quit 事件不会被触发
app.on('quit',()=>{
    
})


//-----------------------------------窗口生命周期-----------------------------------

mainWindow.on('close', () => {
    console.log('close - 窗口关闭,回收窗口引用')
    mainWindow = null
})
// webContents - 渲染以及控制 web 页面
mainWindow.webContents.on('did-finish-load', ()=>{
    console.log('did-finish-load - 导航完成时触发,即选项卡的旋转器将停止旋转,并指派onload事件后')
})

mainWindow.webContents.on('dom-ready', ()=>{
    console.log('dom-ready - dom准备好了,可以选择界面上的元素')
})


//html的docment元素渲染时加载的事件
window.addEventListener('DOMContentLoaded',()=>{
  //获取nodejs版本号
  console.log(process.versions.node)
})

生命周期执行顺序

will-finish-launching
open-url
did-become-active
ready - electron 初始化完成时触发
dom-ready - dom准备好了,可以选择界面上的元素
did-finish-load - 导航完成时触发,即选项卡的旋转器将停止旋转,并指派onload事件后
close - 窗口关闭,回收窗口引用
window-all-closed
before-quit
will-quit
quit

进程之间的通讯之IPC方式

微信截图_20231213232321.png

以下代码是渲染进程与主进程相互通讯

//------------------------------------------------------------
//渲染进程界面中的js代码
const {ipcRenderer} = require('electron')

window.addEventListener('DOMContentLoaded',()=>{
    //渲染进程发送消息到主进程
    ipcRenderer.send('message','my renderer')
    //监听主进程向子进程发送信息
    ipcRenderer.on('reply',(event,arg)=>{
        console.log(event)
        console.log(arg)
    })
})

//-------------------------------------------------------------
//主进程代码
const { app, BrowserWindow,ipcMain } = require('electron')

app.on('ready',()=>{
 const win = new BrowserWindow({
    width: 800, //窗口宽度
    height: 600, //窗口高度
    webPreferences:{ //设置web窗口预选参数配置
        // 开启node模块
        nodeIntegration:true,
        //开启远程模块,让渲染窗口能使用electron Api函数
        enableRemoteModule:true
    }
  })
 //加载到渲染进程中
  win.loadFile('index.html')
  //主进程监听ipc消息
  ipcMain.on('message',(event,arg)=>{
       console.log(event)
       console.log(arg)
       //主进程将消息发送到渲染进程中
       event.reply('reply','hello')
  })  
})

进程之间的通讯之remote方式

提供一种remote方式,它是最简单的主进程和渲染进程的通信。

//在渲染进程调用创建窗口
const {BrowserWindow}  = require('electron').remote

const win = new BrowserWindow({
    width: 300, //窗口宽度
    height: 200, //窗口高度
    webPreferences:{ //设置web窗口预选参数配置
        // 开启node模块
        nodeIntegration:true,
        //开启远程模块,让渲染窗口能使用electron Api函数
        enableRemoteModule:true
    }
})
 win.loadFile('index.html')

进程之间的通讯之第三种方式

let windowId = win.id
let mainWin = BrowserWindow.fromId(windowId)
mainWin.webContents.send('mti','1111')

electron自定菜单

const { app, BrowserWindow , Menu } = require('electron')
//打印当前操作系统
console.log(process.platform)
let menuTemp = [
    {label:'文件',submenu:[ //定义子菜单
        {label:'打开文件',
         click(){ //点击事件
             
         }
        },
        {type:'separator'},
    ]},
    {label:'编辑'},
    {
        lable:'角色',
        submenu:[
            {label:'复制',role:'copy',icon:'./1.png'},
            {label:'剪切',role:'cut'},
            {label:'粘贴',role:'paste'},
            {label:'最小化',role:'minimize'}
        ]    
    },
    {
        lable:'多选菜单',
        submenu:[
            {label:'选项1',type:'checkbox'},
            {label:'选项2',role:'checkbox'},
            {label:'选项3',role:'checkbox'},
            {type:'separator'},
            {label:'item',type:'radio'},
            {label:'window',type:'submenu',role:'windowMenu'},
        ]   
    }
]

//生成一个菜单项
let menu = Menu.BuildFromTemplate(menuTemp)
//将上述的模板绑定到应用
Menu.setApplicationMenu(menu)

electron右键菜单

const { remote } = require('electron')

const Menu = remote.Menu


let contextTemp = [
    {
        label:'Run Code',
        click(){
            
        }
    }
]

//创建菜单对象
let menu = Menu.buildFromTemplate(contextTemp)

window.addEventListener('DOMContentLoaded',()=>{
    window.addEventListener('contextmenu',(env)=>{
        env.preventDefault()
        //按popup方式打开
        menu.popup({window:remote.getCurrentWindow()})
    },false)
})

查找窗口句柄方式

//第一种
// 获取窗口的 ID
let windowId = win.id
BrowserWindow.fromId(windowId)
//第二种
remote.getCurrentWindow()

Dialog模块

弹出对话框窗口

const {remote} = require('electron')

window.onload=()=>{
    let btn1 = document.getElementById('btn1')
    let btn2 = document.getElementById('btn2')
    let btn3 = document.getElementById('btn3')
    
    
     //打开文件选择器窗口
    btn1.addEventListener('click',()=>{
        remote.dialog.showOpenDialog({
            defaultPath:__dirname, //设置当前目录下
            buttonLabel:'请选择', //设置按钮名称
            title:'my opendialog', //设置文件标题名称
            //properties:['openFile'], //选择文件
            properties:['openDirectroy'], //选择目录
            filters: [ //过滤条件
                { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
                { name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
                { name: 'Custom File Type', extensions: ['as'] },
                { name: 'All Files', extensions: ['*'] }
            ]
        }).then((ret)=>{ //选择文件返回数据
            console.log(ret)
        }).catch((err)=>{ //错误信息
           console.log(err) 
        })
    })
    
     //打开保存对话框
    btn2.addEventListener('click',()=>{
        remote.dialog.showSaveDialog({
            defaultPath:__dirname, //设置当前目录下
            buttonLabel:'保存', //设置按钮名称
            title:'my saveDialog', //设置标题名称
            //properties:['openFile'], //选择文件
            properties:['showHiddenFiles'], //显示隐藏文件
            filters: [ //过滤条件
                { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
                { name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
                { name: 'Custom File Type', extensions: ['as'] },
                { name: 'All Files', extensions: ['*'] }
            ]
        }).then((ret)=>{ //成功返回数据
            console.log(ret)
        }).catch((err)=>{ //错误信息
           console.log(err) 
        })
    })
    
     //打开提示对话框
    btn3.addEventListener('click',()=>{
        remote.dialog.showMessageBox({
            type:'info',//none, info, error, question
            message :'保存成功', //设置按钮名称
            title:'my saveDialog', //设置标题名称
        }).then((ret)=>{ //成功返回数据
            console.log(ret)
        }).catch((err)=>{ //错误信息
           console.log(err) 
        })
    })
    
    
}

shell模块

shell 模块提供与桌面集成相关的功能。

const { shell } = require('electron')

//打开系统默认浏览器
shell.openExternal('https://github.com')

//打开资源管理器
shell.showItemInFolder('c:/test')

//打开系统目录文件或文件
shell.openPath('c:/test/1.exe')

//移动到回收站
shell.trashItem('c:/test')

//播放哔哔的声音.
shell.beep()

//创建一个快捷图标
const shortcutPath = 'path/to/shortcut.lnk';

shell.writeShortcutLink(shortcutPath, 'create', {
  target: process.execPath,
  args: ['--myarg'],
  workingDirectory: 'path/to/app',
  icon: 'path/to/app/icon.ico',
  description: 'My Electron App Shortcut'
});

路径操作

__dirname 表示当前编写的文件所处的目录
path.basename() 返回路径的最后一部分
path.dirname() 返回路径的目录部分
path.extname() 返回路径的扩展名部分。
path.isAbsolute() 如果是绝对路径,则返回 true。
path.join() 连接路径的两个或多个部分
path.normalize() 当包含类似 .、.. 或双斜杠等相对的说明符时,则尝试计算实际的路径
path.parse() 解析对象的路径为组成其的片段
root: 根路径。
dir: 从根路径开始的文件夹路径。
base: 文件名 + 扩展名
name: 文件名
ext: 文件扩展名
path.relative()接受 2 个路径作为参数。 基于当前工作目录,返回从第一个路径到第二个路径的相对路径。
path.resolve()可以使用 path.resolve() 获得相对路径的绝对路径计算

文件操作

const fs = require('fs')
//创建新的文件夹
fs.mkdir('creatdir', 0777, function(err){
 if(err){
  console.log(err);
 }else{
  console.log("creat done!");
 }
})


//读取文件
fs.readFile('path/to/file', 'utf-8', (err, data) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log(data); // 文件内容
});


//写入文件
const content = 'Hello, World!';

fs.writeFile('path/to/file', content, 'utf-8', (err) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log('文件写入成功');
});


//复制文件
const sourcePath = 'path/to/source/file';
const destinationPath = 'path/to/destination/file';

fs.copyFile(sourcePath, destinationPath, (err) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log('文件复制成功');
});

//移动文件

const sourcePath = 'path/to/source/file';
const destinationPath = 'path/to/destination/file';

fs.rename(sourcePath, destinationPath, (err) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log('文件移动成功');
});


//删除文件
const filePath = 'path/to/file';

fs.unlink(filePath, (err) => {
  if (err) {
    console.error(err);
    return;
  }

  console.log('文件删除成功');
});

屏幕信息

//参考文档 https://www.electronjs.org/zh/docs/latest/api/screen

const { app, BrowserWindow, screen } = require('electron')

let mainWindow = null

app.whenReady().then(() => {
  // Create a window that fills the screen's available work area.
  const primaryDisplay = screen.getPrimaryDisplay()
  const { width, height } = primaryDisplay.workAreaSize

  mainWindow = new BrowserWindow({ width, height })
  mainWindow.loadURL('https://electronjs.org')
})

渲染以及控制 web 页面

//参考文档 https://www.electronjs.org/zh/docs/latest/api/web-contents
const { BrowserWindow } = require('electron')

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

//获取web上下文
const contents = win.webContents
console.log(contents)

//加载http网页
const options = { extraHeaders: 'pragma: no-cache\n' }
win.webContents.loadURL('https://github.com', options)

//加载文件
const win = new BrowserWindow()
win.loadFile('src/index.html')

//插入样式到web页面
win.webContents.on('did-finish-load', async () => {
  const key = await win.webContents.insertCSS('html, body { background-color: #f00; }')
  win.webContents.removeInsertedCSS(key)
})

//执行页面中JavaScript脚本
win.webContents.executeJavaScript('fetch("https://jsonplaceholder.typicode.com/users/1").then(resp => resp.json())', true)
  .then((result) => {
    console.log(result) // Will be the JSON object from the fetch call
  })


控制页面和内联框架(iframes)

//参考文档 https://www.electronjs.org/zh/docs/latest/api/web-frame-main

const { BrowserWindow, webFrameMain } = require('electron')

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

win.webContents.on(
  'did-frame-navigate',
  (event, url, httpResponseCode, httpStatusText, isMainFrame, frameProcessId, frameRoutingId) => {
    const frame = webFrameMain.fromId(frameProcessId, frameRoutingId)
    if (frame) {
      const code = 'document.body.innerHTML = document.body.innerHTML.replaceAll("heck", "h*ck")'
      frame.executeJavaScript(code)
    }
  }
)

发通知

const {Notification} = require('electron')

new Notification({
    title:'我的通知',
    body:'系统发送一条新的消息请注意查收'
}).show()

系统托盘

//参考文档https://www.electronjs.org/zh/docs/latest/api/tray
const { app, Menu, Tray } = require('electron')

let tray = null
app.whenReady().then(() => {
  tray = new Tray('/path/to/my/icon')
  const contextMenu = Menu.buildFromTemplate([
    { label: 'Item1', type: 'radio' },
    { label: 'Item2', type: 'radio' },
    { label: 'Item3', type: 'radio', checked: true },
    { label: 'Item4', type: 'radio' }
  ])
  tray.setToolTip('This is my application.')
  tray.setContextMenu(contextMenu)
})

录屏模块

//参考文档https://www.electronjs.org/zh/docs/latest/api/desktop-capturer

项目架构

#vite+electron+vue3+typeScript
npm install vite electron-vite --template vue-ts

其他web桌面技术

https://tauri.app/
https://nwjs.io/
//忽略https安全验证
app.commandLine.appendSwitch('--ignore-certificate-errors','true')
app.commandLine.appendSwitch('--disable-web-security','true')
//允许https混合加载http的资源
app.commandLine.appendSwitch('--allow-running-insecure-content','true')
//允许私有安全策略
app.commandLine.appendSwitch('--disable-features=BlockInsecurePrivateNetworkRequests','true')
app.commandLine.appendSwitch('--flag-switches-begin','true')
app.commandLine.appendSwitch('--flag-switches-end','true')
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
其它技术
electron-shared 
miniblink
上一篇 下一篇

猜你喜欢

热点阅读