Electron桌面开发入门

2023-10-27

1.初始化工作
midir electron-demo
cd electron-demo
npm init

//到package.json 文件下将入口文件修改为main.js
  "main": "main.js",
//并且创建main.js文件

//electron 安装依赖
npm i electron -S
//安装nodemon 
npm install nodemon -D

修改package.json

{
  "name": "electron-demo",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --exec electron . --watch ./ --ext .html,.js,.css"    //监听html,js,css文件的变化
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "electron": "^19.0.6",
    "electron-win-state": "^1.1.22"
  },
  "devDependencies": {
    "nodemon": "^2.0.16"
  }
}


修改main.js

//app应用模块
//BrowserWindow 浏览器串口模块
const { app, BrowserWindow } = require('electron')

//创建窗口
const createwindow = ()=>{
  const win = new BrowserWindow({
    width:800,
    height:600
  })

  // 给窗口装载页面
  // win.loadFile('./renderer/index.html')
  win.loadURL('https://cn.bing.com/?mkt=zh-CN')

  //自动打开发开工具
  win.webContents.openDevTools()


  //可以创建多个窗口
//   const win2 = new BrowserWindow({
//     width:800,
//     height:600
//   })

//   win2.loadURL('https://www.baidu.com')
}

//应用就绪,可以装载窗口了
app.whenReady().then(createwindow)

启动程序

npm start

看到效果
在这里插入图片描述
后续操作省略,可以对应文件夹实现
目录如下
在这里插入图片描述
main.js 主程序文件 node环境,能读写文件
renderer 渲染文件 有关页面渲染的都在这里,可以结合vue等框架
app.js //vue模板
index.html //窗口页面
style.css //窗口样式文件
preload-js 桥接文件
因为渲染程序和主程序直接传递消息有严重的安全隐患,所以要通过这里的’桥’文件,当工具人这里面重要的核心就是通过contextBridge 暴露一个对象给渲染程序通过ipcRenderer中的方法监听和抛发事件
controller 主程序事件控制器
主要处理主程序监听事件和抛发事件的逻辑处理
具体操作如下

main.js

// 引入事件处理器,对render的操作作出回应
require('./controller/ipcMessage')

//引入插件    作用:记录窗口的位置大小
const  WinState = require('electron-win-state').default

//app应用模块
//BrowserWindow 浏览器串口模块
const { app, BrowserWindow } = require('electron')
const {resolve} =require('path') 
//创建窗口
const createwindow = ()=>{

  // 设置默认窗口大小
  const  winState = new WinState({ 
    defaultWidth: 1400,
    defaultHeight: 600,
    dev:true
  })

  const win = new BrowserWindow({
    ...winState.winOptions,
    // width:800,
    // height:600,
    show:false,
    minHeight:300,
    minWidth:400,
   
    backgroundColor: 'aliceblue',
    resizable:true, //默认可以 切换窗口大小
    movable:true,  //窗口是否可以移动
    // frame:true,  //是否关闭边框
    // titleBarStyle: "hidden",  // 控制titleBar的状态
    backgroundColor:"skyblue",
    webPreferences:{
      //nodeIntegration 开启node  contextIsolation 开启或关闭主程序和渲染程序的隔离 
      //这两个的开启会有安全隐患 
      // nodeIntegration:true,
      // contextIsolation:false    

      //预加载,桥文件
      preload: resolve(__dirname,'./preload-js')
    }
  })

  //将窗口位置大小存起来
  winState.manage(win)

  // 给窗口装载页面
  win.loadFile('./renderer/index.html')
  // win.loadURL('https://cn.bing.com/?mkt=zh-CN')

  //自动打开发开工具
  // win.webContents.openDevTools()

  //同win 的show一起配置 当win装载完成显示
  win.once('ready-to-show',()=>{
    win.show()
  })
  // 可以创建多个窗口
  // const win2 = new BrowserWindow({
  //   width:800,
  //   height:600,
  //   parent:win,  //配置父窗口,这里是子窗口
  //   modal:false   //锁定子窗口
  // })

  // win2.loadURL('https://www.baidu.com')
  //win2 关闭时 win 全屏
  // win2.on('close',()=>{
  //   win.maximize()
  // })
}

//app主进程完成加载
// app.on('will-finish-launching',()=>{
//   console.log('will-finish-launching')
// })
// //app准备就绪
// app.on('ready',()=>{
//   console.log('ready')
// })

// // app将要退出
// app.on('will-quit',()=>{
//   console.log('will-quit')
// })

// app.on('before-quit',()=>{
//   console.log('before-quit')
// })

// //app退出
// app.on('quit',()=>{
//   console.log('quit')
// })
// //所有窗口都关闭
// app.on('window-all-closed',()=>{
//   console.log('window-all-closed')
//   //对于macos系统,关闭窗口时,不会直接退出应用,还会保留图标
//   // if(process.platform!=='darwin'){
//   //   app.quit()
//   // }
// })


//应用就绪,可以装载窗口了
app.whenReady().then(()=>{
  createwindow()
  //在macos下,当全部窗口都关了,点击图标,窗口再次打开
  app.on('activate',()=>{
    if(BrowserWindow.getAllWindows().length===0){
      createwindow()
    }
  })

  //isReady  app是否装载完成
  console.log(app.isReady())
  //获取路径
  console.log(app.getPath('desktop'))
  console.log(app.getPath('music'))
  console.log(app.getPath('temp'))
  console.log(app.getPath('userData'))

  console.log(BrowserWindow.getAllWindows().length)
})

// 程序失焦
app.on('browser-window-blur',()=>{
  console.log("app blur")
})

// 程序获取焦点
app.on('browser-window-focus',()=>{
  console.log('app focus')
})

// 在html中允许使用第三方资源  设为false 会有警告
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'

renderer/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- unsafe-eval 配置第三方引用安全策略 -->
  <!-- <meta http-equiv="Content-Security-Policy" content="script-src 'self' unsafe-eval"> -->
  <title>Document</title>
  <script src="./vue.global.js"></script>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <h1>hello electron!</h1>
  <input type="range" />
  <div id="root"></div>
  <script src="./app.js"></script>
</body>
</html>

renderer/app.js

const versions = window.myAPI.versions

const app = Vue.createApp({
  template:`
    <p>Chrome version: {{chromeVersion}}</p>
    <p>Node version: {{NodeVersion}}</p>
    <p>Electron version: {{electronVersion}}</p>
    <button @click="sendSyncMsg"> 发送同步消息 </button>
    <button @click="sendAsyncMsg"> 发送异步消息 </button>
  `,

  data(){
    return {
      chromeVersion: versions.chrome,
      NodeVersion: versions.node,
      electronVersion: versions.electron
    }
  },

  methods:{
    // 发送同步消息给桥文件,桥文件转发给主程序
    sendSyncMsg(){
      myAPI.sendSyncMsg("this message from render ")
    },
    // 主进程和渲染进程传递消息 建议IPC通信 全部通过异步
    async  sendAsyncMsg(){
      let result = await myAPI.sendAsyncMsg()
      console.log(result)
    }

  },
  mounted() {
    // 接受main 返回的消息
    // myAPI.recieveSyncMsgUseCb((msg)=>{
    //   console.log(msg)
    //   console.log(this)
    // })
    //promise调用
    // const result = await myAPI.recieveSyncMsgUsePromise()
    // console.log(result,1111)
    myAPI.recieveSyncMsgUsePromise().then((res)=>{
      console.log(res,22222);
    })
  }
})

app.mount("#root")

renderer/style.css

body {
  background-color: pink;
  /* 设置拖拽 */
  user-select: none;
  -webkit-app-region:drag;
}

/* 设置input可拖拽 */
input {
  -webkit-app-region: no-drag;
}

preload-js/index.js

//preload-js下面的文件我把他叫做'桥'文件,桥文件能直接访问到主程序的信息   因为渲染程序不能直接和主程序做通信 有安全隐患
// 通过桥文件让渲染程序能够获取到主程序的信息

const {contextBridge, ipcRenderer} = require('electron')

// 向main发送一个同步消息
const sendSyncMsg = (msg)=>{
  console.log(msg,"发送了一次");
  ipcRenderer.send('sync-send-event',msg)
}

// main收到同步消息抛发事件到这里,通过promise给Vue层
const recieveSyncMsgUsePromise = ()=>{
  return new Promise((resolve,reject) => {
    ipcRenderer.on('recive-sync-event',(event,msg)=>{
      console.log("返回的数据 1")
      resolve(msg)
    })
  })
}

// main收到同步消息抛发事件到这里,通过callback给Vue层(推荐使用这种方法)
const recieveSyncMsgUseCb = (cb) => {
  ipcRenderer.on('recive-sync-event',(event,msg)=>{
    cb(msg)
  })
}

// 发送异步消息
const sendAsyncMsg = async ()=>{
  const result = await ipcRenderer.invoke('my-invokable-ipc')
  return result
}


//通过这里向渲染程序暴露一个对象,这个对象是myAPI
//通过直接暴露ipcRenderer给vue层有些方法会丢失(坑)
contextBridge.exposeInMainWorld('myAPI',{
  versions: process.versions,
  sendSyncMsg,
  recieveSyncMsgUsePromise,
  sendAsyncMsg,
  recieveSyncMsgUseCb
})

controller/ipcMessage.js

// 事件处理器  对于桥文件抛发的事件做回应
const { ipcMain } = require("electron")

ipcMain.on('sync-send-event',(event,msg)=>{
  console.log(msg,'shoudao 1');
  event.reply('recive-sync-event', '我已经收到:' + msg)
  // event.sender.send('a', '我已经收到:' + msg)
})

function somePromise(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('async message from main')
    },2000)
  })
}
// 异步事件监听
ipcMain.handle("my-invokable-ipc", async (ecent,...args)=>{
  const result = await somePromise()
  return result
})
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Electron桌面开发入门 的相关文章

随机推荐

  • RPC 技术及其框架 Sekiro 在爬虫逆向中的应用,加密数据一把梭

    文章目录 什么是 RPC JSRPC Sekiro 优缺点 什么是 RPC RPC 英文 RangPaCong 中文让爬虫 旨在为爬虫开路 秒杀一切 让爬虫畅通无阻 开个玩笑 实际上 RPC 为远程过程调用 全称 Remote Proced
  • LeetCode——036

    Valid Sudoku My Submissions QuestionEditorial Solution Total Accepted 71051 Total Submissions 233215 Difficulty Easy Det
  • AI 大行其道,你准备好了吗?—谨送给徘徊于转行 AI 的程序员

    前言 近年来 随着 Google 的 AlphaGo 打败韩国围棋棋手李世乭之后 机器学习尤其是深度学习的热潮席卷了整个 IT 界 所有的互联网公司 尤其是 Google 微软 百度 腾讯等巨头 无不在布局人工智能技术和市场 百度 腾讯 阿
  • 学习Javascript闭包(Closure)[非常棒的文章]

    作者 阮一峰 日期 2009年8月30日 闭包 closure 是Javascript语言的一个难点 也是它的特色 很多高级应用都要依靠闭包实现 下面就是我的学习笔记 对于Javascript初学者应该是很有用的 一 变量的作用域 要理解闭
  • 关于论青少年尽早学少儿编程之说

    关于论青少年尽早学少儿编程之说 正如一本书中所描述的一句话 尽早学习编程 是孩子为未来做好准备必不可少的一步 看完这句话之后 给我们的直观印象可能就是 不教孩子学习编程在某种程度上等于不教他们读书写字 这种说法明显是片面的 编程 读书写字
  • 若依系统注册功能

    加油 三步实现注册 前端 后端 分配角色 总结 前端 login vue中打开注册开关 后端 打开数据库sys config表 开启注册功能 分配角色 在SysUserMapper中添加方法 实现方法 在SysUserServiceImpl
  • dialog中二维码显示问题

    由于dialog加载过程会耗费一定时间 因此在dialog中直接调用会导致在一次打开的dialog无法加载二维码 在dialog标签中加入 opened ShowQRCode 属性 opened是dialog动画打开完毕之后的回调 当页面加
  • 计算机网络层提供的面向连接服务还是无连接服务讨论与思考

    概要 在计算机网络领域 网络层应该向运输层提供怎样的服务 面向连接 还是 无连接 曾引起了长期的争论 争论焦点的实质就是 在计算机通信中 可靠交付应当由谁来负责 是网络还是端系统 介绍 有些人认为应当借助于电信网的成功经验 让网络负责可靠交
  • 计算机主机名与用户名区别

    一 主机名概念 主机名就是计算机的名字 计算机名 网上邻居就是根据主机名来识别的 这个名字可以随时更改 从我的电脑属性的计算机名就可更改 用户登陆时候用的是操作系统的个人用户帐号 这个也可以更改 从控制面板的用户界面里改就可以了 这个用户名
  • 1. Inna and Pink Pony

    1 Inna and Pink Pony 首先找出四个边界点 但要注意当横纵坐标等于边界横纵坐标时 需考虑是否会出界 满足以上条件时 考虑横纵坐标移动次数其和为偶数时便可以完成移动 因为正负抵消原则 话不多说 直接上Python代码 n m
  • 解决 CommandNotFoundError: Your shell has not been properly configured to use ‘conda activate’问题

    针对使用conda进入虚拟环境时遇到的问题 CommandNotFoundError Your shell has not been properly configured to use conda activate 解决方法 win r
  • 解决Android中使用RecyclerView滑动时底部item显示不全的问题

    感觉这个bug是不是因人而异啊 找了很多文章都没能解决我的问题 包括在RecyclerView上在嵌套上一层RelativeLayout 添加属性android descendantFocusability blocksDescendant
  • 解决“L6200E Symbol xx defined (by xx.o and xx.o)”重复定义问题

    今天来分享一个关于自己之前遇到的一个问题 就是关于重复定义会造成的一个错误 错误提示为 OBJ LCD axf Error L6200E Symbol ascii 1206 multiply defined by lcd user o an
  • C语言每日一题:7.寻找数组中心下标。

    思路一 暴力求解 1 定义一个ps作为中间下标去记录下标值 2 循环下标ps从头到位 定义四个变量分别是left sum left right sum right 3 初始化left ps 1和right ps 1 当ps0 gt 就让su
  • etcd学习和实战:4、Java使用etcd实现服务发现和管理

    etcd学习和实战 4 Java使用etcd实现服务发现和管理 文章目录 etcd学习和实战 4 Java使用etcd实现服务发现和管理 1 前言 2 代码 2 1 服务注册 2 2 服务发现 2 3 运行结果 2 4 问题 3 最后 1
  • 关于SVM的一点笔记

    关于SVM的一点笔记 一 简单了解 1 感知机 perceptron 感知机是一种类似于生物中神经细胞功能的人工神经元 它可以把一个或者多个输入 x 1 x 1 x1 x
  • flask最基础的增删改查实现步骤及代码

    分类序列化器 写入要序列化的字段 user info id fields Integer name fields String 商品序列化器 写入要序列化的字段 goods info id fields Integer name field
  • Spring系列面试题(Spring、SpringMvc、SpringBoot)

    一 springboot自动配置原理 自动装配 简单来说就是自动把第三方组件的Bean装载到Spring IOC器里面 不需要开发人员再去写Bean的配置 在Spring Boot应用里面 只需要在启动类加上 SpringBootAppli
  • 五张图带你理解 RocketMQ 顺序消息实现机制

    大家好 我是君哥 今天聊一聊 RocketMQ 的顺序消息实现机制 在有些场景下 使用 MQ 需要保证消息的顺序性 比如在电商系统中 用户提交订单 支付订单 订单出库这 3 个消息应该保证顺序性 如下图 对于 RocketMQ 来说 主要是
  • Electron桌面开发入门

    1 初始化工作 midir electron demo cd electron demo npm init 到package json 文件下将入口文件修改为main js main main js 并且创建main js文件 electr