• Electron笔记


    基础环境搭建

    官网:https://www.electronjs.org/zh/

    这一套笔记根据这套视频而写的

    创建项目

    方式一:

    官网点击GitHub往下拉找到快速入门就能看到下面这几个命令了

    git clone https://github.com/electron/electron-quick-start  //克隆项目
    cd electron-quick-start  //切换到项目目录
    npm install //这里可能会有报错问题,本人查了gpt说是什么连接远程失败,这里本人建议使用cnpm淘宝镜像,没有的可以百度安装一下,yarn也不行(亲测yarn安装还是报错)
    npm start //启动项目
    
    • 1
    • 2
    • 3
    • 4

    目录结构

    在这里插入图片描述

    方式二:

    先安装

    cnpm install --save-dev electron   //这里也是建议使用cnpm淘宝镜像
    
    • 1

    直接使用npm或者yarn都可能会报错,官方也有解释,这里本人使用cnpm就没问题

    在这里插入图片描述

    然后在package.json里面创建如下内容

    {
      "name": "my-electron-app",
      "version": "1.0.0",
      "description": "Hello World!",
      "main": "main.js", //这里是主入口
      "author": "萧寂",
      "license": "MIT",
      "scripts": {
        "start": "electron ." //运行的命令
      },
      "devDependencies": {
        "electron": "^26.0.0"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如果安装了nodemon,可以在script节点进行如下配置,可以监听js的代码并实时变化
    在html代码改变后不会立即监听,需要切换到应用里面按下CTRL+R即可刷新

    "scripts": {
      "start": "nodemon --watch main.js --exec npm run build",
      "build": "electron ."
    },
    
    • 1
    • 2
    • 3
    • 4

    在同级创建main.js,然后在main.js中插入以下内容

    const { app, BrowserWindow } = require("electron");
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        width: 800,
        height: 600,
      });
      //当前窗口显示的页面
      win.loadFile("index.html");
    };
    
    // app启动之后创建窗口
    app.on("ready", () => {
      console.log("窗口加载");
      createWindow();
    });
    
    // 生命周期
    // 通过on监听事件
    
    // 监听关闭的
    app.on("close", () => {
      console.log("当前窗口关闭");
    });
    
    app.on("window-all-closed", () => {
      console.log("所有窗口关闭");
      //退出应用
      app.quit();
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    CTRL+SHIFT+I可以打开调试面板
    同级下创建index.html文件

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <!-- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP -->
        <meta
          http-equiv="Content-Security-Policy"
          content="default-src 'self'; script-src 'self'"
        />
        <title>萧寂</title>
      </head>
      <body>
        <h1>你好!</h1>
        我们正在使用 Node.js <span id="node-version"></span>, Chromium
        <span id="chrome-version"></span>, 和 Electron
        <span id="electron-version"></span>.
      </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    然后直接运行

    npm start
    
    • 1

    目录结构

    在这里插入图片描述

    因为第二种方式目录结构简单,所以在这里演示的所有代码都以第二种创建方式基础上写的

    electron生命周期事件

    ready:app初始化完成  //重要
    dom-ready:一个窗口中的文本加载完成  //重要
    did-finsh-load:导航完成时触发   //重要
    window-all-closed:所有窗口都被关闭时触发  //重要
    before-quit:在关闭窗口之前触发
    will-quit:在窗口关闭并且应用退出时触发
    quit:当所有窗口被关闭时触发
    close:当窗口关闭时触发,此时应删除窗口引用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    main.js代码

    const { app, BrowserWindow } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        width: 800,
        height: 600,
      })
      //当前窗口显示的页面
      win.loadFile("index.html")
    
      // 这个webContents对象可以控制dom元素加载事件
      win.webContents.on('did-finish-load', () => {
        console.log('3333->did-finish-load')
      })
      win.webContents.on('dom-ready', () => {
        console.log('2222->dom-ready')
      })
      // 窗口关闭
      win.on('close', () => {
        console.log('8888->close')
        // 从性能考虑,应该释放窗体这个变量,删除窗体引用
        win = null
      })
    }
    
    // 生命周期
    // 通过on监听事件
    app.on('ready', () => {
      console.log("1111->ready")
      createWindow()
    })
    
    app.on("window-all-closed", () => {
      // 如果监听了window-all-closed这个事件,需要在事件里面主动退出应用,没有监听事件的话默认会直接退出应用
      // 但如果监听了此事件,但没有退出操作的话,后续的567生命周期也不会执行
      console.log("4444->window-all-closed")
      //退出应用
      app.quit()
    })
    
    app.on("before-quit", () => {
      console.log("5555->before-quit")
    })
    
    app.on("will-quit", () => {
      console.log("6666->will-quit")
    })
    
    app.on("quit", () => {
      console.log("7777->quit")
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    从打开窗体到关闭窗体打印结果如下

    在这里插入图片描述

    创建窗体时所携带的一些属性

    也是main.js代码

    const { app, BrowserWindow } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100, 
        y: 50, //窗体坐标
        show: false, //不展示窗体
        width: 800,
        height: 600, //长宽
        maxHeight: 600,
        maxWidth: 1000, //最大宽高
        minHeight: 200,
        minWidth: 300, //最小宽高
        resizable: false, //不允许缩放
        title: "萧寂", //标题(加上这个属性,在页面中就不要有title标签了)
        icon: "./及格.png", //设置icon图标
        // frame: false, //只保留主体部分,不保留其他的选项卡窗口了,隐藏菜单栏
        // transparent: true, //将窗体完全透明化
        autoHideMenuBar: true, //只保留标题,不保留其他的选项卡部分
      })
    
      // show需要设置false,意思就是默认不显示窗体,然后执行下面这个事件,ready-to-show:等待完毕准备加载执行,适用于页面显示,监听到了再执行show()
      win.on('ready-to-show', () => {
        win.show()
      })
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    窗口标题及环境(创建窗口)

    这里要说明一下,低版本直接可以使用一个remote,主进程稍微配置一下就能创建窗口了,高版本就不行了,高版本需要安装一个@electron/remote模块,通过对这个模块稍微配置一下也能创建窗口了,本人之前版本是"electron": "^26.0.0","@electron/remote": "^2.0.11",[具体配置可以可以看看这个小哥的博客](https://blog.csdn.net/qq_39077394/article/details/125667918?ops_request_misc=%7B%22request%5Fid%22%3A%22169651971016800213043799%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=169651971016800213043799&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-1-125667918-null-null.142v94insert_down28v1&utm_term=enableRemoteModule%3A true不能使用remote&spm=1018.2226.3001.4187),我下面的代码和这两个不一样我是在主进程创建窗口,渲染进程向主进程发请求才能创建窗体(下面代码示例就是这个方法),因为方式和另两种不一样,因此记录一下

    ctrl+r 可以刷新当前窗口的index.html样式,ctrl+shift+i可以打开调试窗口

    这里强调一下main.js为主进程,创建的js里面重新创建的窗口为渲染进程

    主进程main.js代码如下

    const { app, BrowserWindow, ipcMain } = require("electron") // ipcMain用于渲染进程创建窗体使用
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
        }
      })
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    // 下面代码就是创建渲染进程窗体代码
    // 在主进程中监听渲染进程的请求
    // open-window后面的回调函数,参数一默认是事件对象,参数二为渲染进程传递来的数据
    // pageFileName为ipcRenderer.send()的第二个参数,ipcRenderer.send()由渲染进程发起,参数一为事件名,参数二为页面配置(大小,位置等等)
    ipcMain.on('open-window', (event, winconfig) => {
      console.log('winconfig', winconfig)
      // 创建新窗口并设置相应的配置(配置由渲染进程提供)
      let newWindow = new BrowserWindow(winconfig)
      // 这里设置的是winconfig.pageFileName,所以渲染进程的请求的配置中必须pageFileName代表页面
      newWindow.loadFile(winconfig.pageFileName)
      // 监听创建的窗体关闭事件
      newWindow.on('close', () => {
        console.log('close')
        newWindow = null
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    主进程页面index.html代码

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      <!-- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP -->
      <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
      <title>萧寂</title>
    </head>
    
    <body>
      <h1>窗口标题</h1>
      <button id="btn">打开第一个新窗口</button>
      <button id="btn2">打开第二个新窗口</button>
      <!-- 这里必须要使用外联,不能直接在下面写,不然会报错,大概意思就是不能使用内联脚本 -->
      <script src="./index.js"></script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    渲染进程index.js代码

    // 这里强调一下main.js为主进程,窗口里面页面点击创建的js里面重新创建的窗口为渲染进程
    // require直接使用会报错,因为electron是不被允许直接require的,不给这个权限,需要我们自行放开
    // 权限需要在窗口的配置定义webPreferences对象,值为 {nodeIntegration: true,contextIsolation: false},这样就可以正常使用require了
    
    // 创建窗口这里使用的是electron自带的ipcRenderer属性,它是向主进程发送创建窗体请求,参数一为事件名,参数二为窗体配置
    const { ipcRenderer } = require("electron")
    const path = require("path")
    window.addEventListener("DOMContentLoaded", () => {
      // 点击按钮打开新窗口
      // 获取btn
      const btn = document.getElementById("btn")
      // 按钮点击打开新窗口
      btn.addEventListener("click", () => {
        // 创建新窗口(向主进程发起请求,创建窗体,并显示pageFileName指定的页面)
        ipcRenderer.send('open-window', {
          width: 600,
          height: 400,
          webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
          },
          pageFileName: path.join(__dirname, "list.html") // 确保传递了正确的页面文件名,list.html需要显示的页面
        })
      })
    
    
      // 打开第二个窗口
      // 获取btn
      const btn2 = document.getElementById("btn2")
      // 按钮点击打开新窗口
      btn2.addEventListener("click", () => {
        // 创建新窗口(向主进程发起请求,创建窗体,并显示pageFileName指定的页面)
        ipcRenderer.send('open-window', {
          width: 200,
          height: 200,
          webPreferences: {
            nodeIntegration: true,
            contextIsolation: false
          },
          pageFileName: path.join(__dirname, "list2.html") // 确保传递了正确的页面文件名,list2.html需要显示的页面
        })
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    项目结构

    在这里插入图片描述

    效果图

    在这里插入图片描述

    自定义窗口的实现(以及阻止窗口关闭)

    项目结构

    在这里插入图片描述

    安装

    npm install --save @electron/remote
    
    • 1

    main.js代码如下

    const { app, BrowserWindow } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        frame: false, // 只保留主体部分,然后后面的样式全部都是由html去模拟
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
    
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    
    
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    index.html

    DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      
      <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
      <title>萧寂title>
      <link rel="stylesheet" href="./index.css">
    head>
    
    <body>
      <main>
        <h1 class="box">主题内容h1>
        <div>
          <span>span>
          <span>span>
          <span>Xspan>
        div>
      main>
      <h2>上面的三个模拟最小化,最大化和关闭h2>
    
      <div class="isShow">
        <h3>是否关闭当前应用h3>
        <p>系统可能不会保存您的所有更改p>
        <p><span class="s1">span><span class="s1">span>p>
      div>
      <script src="./index.js">script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    index.css

    main {
      display: flex;
      justify-content: space-evenly;
      align-items: center;
    }
    .box {
      color: red;
    }
    span {
      padding: 10px 5px;
      cursor: pointer;
    }
    /* 将提示语隐藏 */
    .isShow {
      display: none;
    }
    .s1 {
      cursor: pointer;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    index.js

    const remote = require("@electron/remote")
    
    window.addEventListener('DOMContentLoaded', () => {
    //利用remote获取当前窗口对象
      let mainwin = remote.getCurrentWindow()
      
      // 这里代码是当窗口关闭,去进行一下阻止,并弹出提示框,用户确定关闭再进行关闭
      // 监听close事件,close事件触发会执行这个事件onbeforeunload
      window.onbeforeunload = function () {
        // 获取到弹框dom元素,并设置样式
        document.querySelector('.isShow').style.display = 'block'
        // 将主题内容隐藏
        document.querySelector('h2').style.display = 'none'
    
        // 获取弹窗的按钮(确认和取消)
        let btn = document.querySelectorAll('.s1')
    
        // 点击确认关闭按钮
        btn[0].addEventListener('click', () => {
          // 这里不再使用close事件,不然会一直触发window.onbeforeunload事件,进入死循环了
          mainwin.destroy() //窗口销毁
        })
    
        // 点击取消按钮
        btn[1].addEventListener('click', () => {
          // 将窗口隐藏就好了
          // 获取到弹框dom元素,并设置样式
          document.querySelector('.isShow').style.display = 'none'
          // 将主题内容显示
          document.querySelector('h2').style.display = 'block'
        })
        return false
      }
    
      const spans = document.querySelectorAll('span')
    
      // 最小化
      spans[0].addEventListener("click", () => {
        mainwin.minimize() //窗口最小化
      })
      // 放大
      spans[1].addEventListener("click", () => {
        // 最大化操作
        console.log('mainwin.isMaximized()', mainwin.isMaximized())  //false,返回布尔值,代表当前界面是否是最大化了
        if (!mainwin.isMaximized()) {
          mainwin.maximize() //如果没有最大化的话,给个最大化
        } else {
          mainwin.restore() //如果是最大化了,给它恢复到初始状态
        }
      })
      // 关闭窗口
      spans[2].addEventListener("click", () => {
        mainwin.close()  //关闭窗口
      })
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    效果图

    在这里插入图片描述

    当点击关闭按钮,会弹出提示框,点击是就关闭,点击否会将提示框进行隐藏
    在这里插入图片描述

    父子及模态窗口

    模态窗口定义:定义完以后不能对主窗口或者别的窗口进行操作,除非模态窗口
    其余代码和上面一样,只修改了index.js代码,代码如下

    const remote = require('@electron/remote')
    
    window.addEventListener('DOMContentLoaded', () => {
      let btn = document.querySelector('#btn')
      btn.addEventListener('click', () => {
        let subWin = new remote.BrowserWindow({
          width: 200,
          height: 200,
          parent: remote.getCurrentWindow(), //这个属性指向父级,实现了父子关联
          modal: true,  //定义模态窗口(默认为false,定义完以后不能对主窗口或者别的窗口进行操作,除非关闭模态窗口)
        })
        subWin.loadFile('sub.html')
        subWin.on('close', () => {
          subWin = null
        })
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    自定义菜单

    main.js代码如下

    const { app, BrowserWindow, Menu } = require("electron") //Menu是菜单模块
    
    console.log(process.platform)
    
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50,
        title: '自定义菜单',
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,
          enableRemoteModule: true,
        }
      })
    
      // 1.这里定义菜单(定义自己需要的菜单项)
      let menuTemp = [
        {
          label: '文件',
          submenu: [ //定义二级菜单
            {
              label: '打开文件',
              click () {
                // 这里就可以使用每个选项的点击事件
                console.log('当前需要的就是打开某一具体的文件')
              }
            },
            {
              type: 'separator'  // 添加分割线,就这一个属性将上下分隔开
            },
            {
              label: '关闭文件夹'
            },
            {
              label: '关于',
              role: 'about' //弹出关于项
            }
          ]
        },
        {
          label: '编辑'
        }
      ]
      // 2.利用上面菜单项生成一个菜单
      let menu = Menu.buildFromTemplate(menuTemp)
      // 3.将上述的自定义菜单添加到应用里面
      Menu.setApplicationMenu(menu)
    
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
      win.loadFile("index.html")
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    
    
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    这里打印有点中文乱码问题,解决方法就是在终端输入chcp 65001回车,重新执行即可解决中文乱码问题

    菜单角色及类型

    main.js代码

    const { app, BrowserWindow, Menu } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
    
      //  1.自定义菜单项
      let menutap = [
        {
          label: '角色',
          submenu: [
            { label: '复制', role: 'copy' },
            { label: '剪切', role: 'cut' },
            { label: '粘贴', role: 'paste' },
            { label: '最小化', role: 'minimize' },
          ]
        },
        {
          label: '类型',
          submenu: [
            { label: '多选一', type: 'checkbox' },
            { label: '多选二', type: 'checkbox' },
            { label: '多选三', type: 'checkbox' },
            { type: 'separator' },
            { label: '单选1', type: 'radio' },
            { label: '单选2', type: 'radio' },
            { label: '单选3', type: 'radio' },
            { type: 'separator' },
            { label: 'windows', type: 'submenu', role: 'windowMenu' } //这里两个属性必须同时给出
          ]
        },
        {
          label: '其他',
          submenu: [
            {
              label: '打开',
              icon: '',
              accelerator: 'ctrl + o', //定义快捷键
              click () {
                console.log('打开操作执行了')
              }
            }
          ]
        }
      ]
      // 2.依据上述的数据创建一个menu
      let menu = Menu.buildFromTemplate(menutap)
      // 3.将上述菜单添加至app身上
      Menu.setApplicationMenu(menu)
    
    
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    
    
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    在这里插入图片描述

    动态创建菜单

    index.html代码如下

    DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      
      <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
      <title>萧寂title>
    head>
    
    <body>
      <h1>动态创建菜单h1>
      <button>创建自定义菜单button>
      <hr>
      <input type="text" placeholder="输入自定义菜单项内容" name="" id="">
      <button>添加菜单项button>
      <script src="./index.js">script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    index.js代码如下

    const remote = require('@electron/remote')
    // 找到菜单和菜单项
    const Menu = remote.Menu
    const MenuItem = remote.MenuItem
    window.addEventListener('DOMContentLoaded', () => {
      // 获取按钮
      let btn = document.querySelectorAll('button')
      // 获取输入框
      let input = document.querySelector('input')
    
      // 自定义全局变量存放菜单项
      let menuItem = new Menu()
      // 点击生成自定义菜单
      btn[0].addEventListener('click', () => {
        // 创建菜单
        let menuFile = new MenuItem({ label: '文件', type: 'normal' })
        let menuEdit = new MenuItem({ label: '编辑', type: 'normal' })
        let customMenu = new MenuItem({ label: '自定义菜单项', submenu: menuItem })
        // 将创建好的菜单添加到menu
        let menu = new Menu()
        menu.append(menuFile)
        menu.append(menuEdit)
        menu.append(customMenu)
        // 将ment放置于app中显示
        Menu.setApplicationMenu(menu)
      })
    
      // 点击动态添加菜单项
      btn[1].addEventListener('click', () => {
        // 获取当前input输入框当中输入的内容
        let con = input.value.trim()
        if (con) {
          menuItem.append(new MenuItem({ label: con, type: 'normal' }))
          input.value = ''
        }
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    效果图
    在这里插入图片描述
    点击自定义创建菜单会替换原来的菜单项,在输入框输入菜单名,点击添加菜单项会在菜单栏的自定义菜单里面追加自己添加的菜单项

    自定义右键菜单

    所有代码都是在index.js里面写的,index.html无代码只引用了index.js
    index.js代码如下

    const remote = require('@electron/remote')
    const Menu = remote.Menu
    
    // 01 创建一个自定义菜单的内容
    let contextTemp = [
      { label: 'RunCode' },
      { label: '转到定义' },
      { type: 'separator' },
      {
        label: '其他功能',
        click () {
          console.log("其他功能选项被点击了")  //这个打印不会在主进程显示,而是在桌面版使用ctrl+shifl+i去控制台看
        }
      }
    ]
    
    // 02 依据上述内容来创建menu
    let menu = Menu.buildFromTemplate(contextTemp)
    
    window.addEventListener('DOMContentLoaded', () => {
      // 03 在鼠标右击行为发生后显示出来
      window.addEventListener('contextmenu', (e) => {
        e.preventDefault() //阻止有些元素点击的默认行为
        menu.popup({ window: remote.getCurrentWindow() })  //将当前窗口对象作为popup参数,代表在当前窗口弹出
      }, false)
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    主进程和渲染进程进行通信

    同步和异步进行通信

    index.html

    DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      
      <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
      <title>萧寂title>
    head>
    
    <body>
      <h1>渲染进程与主进程进行通信h1>
      <hr>
      <button>渲染主异步操作button>
      <hr>
      <button>渲染主同步操作button>
      <script src="./index.js">script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    index.js

    const { ipcRenderer } = require('electron')
    
    window.onload = () => {
      // 获取元素
      let abtn = document.querySelectorAll('button')
    
      // 01 采用异步的API在渲染进程中给主进程发送消息
      abtn[0].addEventListener('click', () => {
        ipcRenderer.send('msg1', 'dataMag')
      })
    
      // 当前接收主进程的消息
      ipcRenderer.on('msgRe', (ev, data) => {
        console.log('data', data) //主进程回复的异步消息
      })
    
    
      // 02 采用同步的方式完成数据的通信
      abtn[1].addEventListener('click', () => {
        let val = ipcRenderer.sendSync('msg2', 'SyncMsg')
        console.log('val', val)  // 主进程回复的同步消息
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    主进程main.js

    const { app, BrowserWindow, ipcMain } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    // 监听渲染进程发送的消息
    ipcMain.on('msg1', (e, data) => {
      console.log('data', data)
    
      // 主进程给渲染进程发消息
      e.sender.send('msgRe', "主进程回复的异步消息")
    })
    
    // 监听渲染进程发送的消息
    ipcMain.on('msg2', (e, data) => {
      console.log('data', data)
    
      // // 主进程给渲染进程发消息
      e.returnValue = '主进程回复的同步消息'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    通过主进程主动发送消息控制渲染进程作出行为

    以下代码没有使用index.html,index.html只是引用了一下index.js
    index.js

    const { ipcRenderer } = require('electron')
    
    window.onload = () => {
    
      // 当前接收主进程的消息
      ipcRenderer.on('mtp', (ev, data) => {
        console.log('data', data) //主进程回复的异步消息
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    main.js主进程代码

    const { app, BrowserWindow, ipcMain, Menu } = require("electron")
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
    
    
      // 主要就是下面这一块
      // 定义菜单
      let temp = [
        {
          label: 'send',
          click () {
            // 点击发送消息,getFocusedWindow获取渲染进程窗口
            BrowserWindow.getFocusedWindow().webContents.send('mtp', '来自主进程发送来的消息')
          }
        }
      ]
      let menu = Menu.buildFromTemplate(temp)
      Menu.setApplicationMenu(menu)
      // 添加了菜单发现ctrl+shift+i不能打开控制台了,因为将原生东西替换掉就不能使用里面的快捷键了
      // 可以使用下面的win.webContents可以控制窗口的所有内容
      win.webContents.openDevTools()  //可以直接运行项目看到控制台
    
    
    
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这里插入图片描述
    这段代码就是点击菜单的send可以向渲染进程发送消息

    渲染进程间的通信

    基于本地存储的渲染进程通信

    项目结构
    在这里插入图片描述

    index.html代码如下

    DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      
      <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
      <title>萧寂title>
    head>
    
    <body>
      <h1>渲染进程间通信h1>
      <button>打开窗口2button>
      <script src="./index.js">script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    index.js代码如下:

    const { ipcRenderer } = require('electron')
    
    window.onload = () => {
      // 获取元素
      let btn = document.querySelector('button')
    
      // 向主进程发送消息
      btn.addEventListener('click', () => {
        ipcRenderer.send('openWin2')
    
        // 打开窗口二之后保存数据
        localStorage.setItem('name', '萧寂')
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    main.js代码如下

    const { app, BrowserWindow, ipcMain } = require("electron")
    
    // 定义全局变量,存放主窗口id
    let mainwinid = null
    
    const createWindow = () => {
      // 创建窗口
      let win = new BrowserWindow({
        x: 100,
        y: 50, //窗体坐标 
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
      require('@electron/remote/main').initialize()
      require("@electron/remote/main").enable(win.webContents)
      //当前窗口显示的页面
      win.loadFile("index.html")
      // 获得主窗口id
      mainwinid = win.id
      // 窗口关闭
      win.on('close', () => {
        console.log('close')
        win = null
      })
    }
    // 窗口加载和关闭
    app.on('ready', createWindow)
    app.on("window-all-closed", () => {
      console.log("window-all-closed")
      app.quit()
    })
    
    // 接收其他进程发送的数据,然后完成后续的逻辑
    ipcMain.on('openWin2', () => {
      // 接收到渲染进程中按钮点击信息之后完成窗口2 的打开
      let subwin1 = new BrowserWindow({
        width: 400,
        height: 300,
        parent: BrowserWindow.fromId(mainwinid),  //代表将index.html主窗口作为这个subwin1的父窗口,这样父窗口关闭,subwin1也能跟着关闭
        webPreferences: {
          nodeIntegration: true,
          contextIsolation: false,  //加入这两行代码就可以正常使用require了,不会报错了
          enableRemoteModule: true
        }
      })
      subwin1.loadFile('subwin1.html')
      subwin1.on('close', () => {
        subwin1 = null
      })
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    subwin1.html代码如下

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body>
      <h2>当前窗口2</h2>
      获取到index.html中的数据:<input type="text" />
      <script src="./subwin1.js"></script>
    </body>
    
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    subwin1.js代码如下:

    window.onload = () => {
      let input = document.querySelector('input')
      let val = localStorage.getItem('name')
      input.value = val
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    效果图
    在这里插入图片描述
    剩下的有空再更新吧,基础的笔记基本全了,不过原生electron去写可能性不大,大部分都会以vue等框架写完打包到electron去运行,以上所有的笔记基本满足需求,剩下的等我真正需要用到原生electron时候再继续更新吧,各位可以看上面我链接那个视频继续学习,这套笔记截止到那个视频的第16集该第17集了,我这套笔记就是根据那套视频做的

    这里附赠一下electron+vue3+vite打包桌面版的一套视频,外加笔记
    electron+vue3+vite视频教程
    视频同步笔记,这里有三个系列,按照顺序去看

  • 相关阅读:
    了解策略模式
    RocketMQ 延迟消息解析——图解、源码级解析
    Midjourney 商用实战升级版 AI给模特换脸
    如何使用 Fail2ban 防止对 Linux 的暴力攻击?
    ubuntu 修改磁盘名称
    linux防火墙相关命令
    C++多线程的用法(包含线程池小项目)
    Linux DRM modesetting API分析
    JS逆向之浏览器补环境详解
    3 个开源项目,让你感受程序员的浪漫!
  • 原文地址:https://blog.csdn.net/weixin_68658847/article/details/133610360