当前位置: 首页 > article >正文

Electron入门笔记

Electron入门笔记

  • Electron
    • Electron 是什么
    • Electron流程模型
    • 创建第一个Electron项目
    • 配置自动重启
    • 主进程和渲染进程通信
    • 打包应用

Electron

Electron 是什么

  • 跨平台的桌面应用开发框架
  • 使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium和 Node.js

Electron流程模型

  • 主进程:

    • 主进程只有一个
    • 主要功能是管理渲染进程,与操作系统打交道。调用Native API 进行各种系统级的操作,并且可以跨平台
    • node环境,能使用node的各种API,使用node的各个模块进行操作,没有浏览器相关的属性,无法访问window等浏览器属性
      在这里插入图片描述
      渲染进程:
  • 渲染进程有多个

  • 主要是程序的展示的窗口

  • 浏览器环境,本质就是Chromium,能使用浏览器的各种API,无法使用node环境的API(ctrl shift i可以打开窗口的控制台,跟浏览器是一模一样的,ctrl R可以刷新页面)
    在这里插入图片描述

  • 主进程和渲染进程是可以通信的,可以是渲染进程向主进程单向通信,也可以是主进程想渲染进程通信,也可以双向通信,进程间的通信称为IPC

  • 总结:主进程(一个)管理各个渲染进程(多个),渲染进程跟主进程进行通信(渲染进程间无法直接通信,通过主进程作为中间人可以实现),主进程调用各种Native API 完成系统级操作

创建第一个Electron项目

  • 环境:需要先安装Node.js

    • 注意:先安装Node.js只是为了正确安装electron, 因为 Electron 将 Node.js 嵌入到其二进制文件中,你应用运行时的 Node.js 版本与你系统中运行的 Node.js 版本无关
  • mkdir my-electron-app && cd my-electron-app
    yarn init
    
  • 执行yarn init生成package.jsonauthordescription在打包阶段属于必填项

  • {
      "name": "electron-first",
      "version": "1.0.0",
      "main": "main.js",
      "license": "MIT",
      "author": "XiaoMing",
      "description": "Hello World",
    }
    
    
  • 安装Electron并在package.json配置启动命令

    • yarn add ELectron -D
      
    • {
        "scripts": {
          "start": "electron ." // 注意 . 不能省略
        }
      }
      
    • 启动项目前需要创建主进程,也就是package.json中对应的main,命名为main.js, 否则会报错,创建完成后,执行yarn start即可启动

    • 此时启动无报错,但也无任何反应,因为没有在主进程里配置任何东西,渲染窗口等进程需要在主进程中配置

  • 创建pages文件夹用于存放页面文件(也就是渲染进程的文件),分别创建对应的index.html,index.css,render.js文件

    • index.html中引入cssjs文件

    • htmlhead需要配置一个meta,解决CSP的警官

      • <meta
          http-equiv="Content-Security-Policy"
          content="default-src 'self'; script-src 'self' 'unsafe-inline'"
        />
        

在这里插入图片描述

      • default-src 'self'是一组配置,意思是如果不做出任何的说明,引入的外部资源,只能是属于同源
        script-src 'self' 'unsafe-inline'是另一组配置,指引入样式的时候,可以有两种写法,第一种写法
        self 是可以引入同源的样式表,第二种写法unsafe-inline 可以使用行内样式
  • <!DOCTYPE html>
    <html lang="en">
      <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' 'unsafe-inline'"
        />
        <title>Hello World!</title>
        <link rel="stylesheet" href="./index.css" />
      </head>
      <body>
        <h1>Hello World!</h1>
        We are using Node.js <span id="node-version"></span>, Chromium
        <span id="chrome-version"></span>, and Electron
        <span id="electron-version"></span>.
        <script src="./render.js"></script>
      </body>
    </html>
    
    

在这里插入图片描述
需要将写好的页面加载出来,如何加载页面,这个需要主进程进行控制,因此需要配置主进程main.js

  • const { app, BrowserWindow } = require("electron");
    
    const createWindow = () => {
      const win = new BrowserWindow({
        width: 800,
        height: 600,
      });
      win.loadFile("./pages/index.html");
    };
    
    // app.whenReady().then(() => {}) 的写法与 app.on("ready", () =>{}) 同理
    
    app.on("ready", () => {
      createWindow();
      app.on("window-all-closed", () => {
        if (process.platform !== "darwin") app.quit();
      });
    });
    

在这里插入图片描述

  • 管理窗口的生命周期

    • 关闭所有窗口时退出应用 (Windows & Linux),在Windows和Linux上,关闭所有窗口通常会完全退出一个应用程序。

      // 为了实现这一点,你需要监听 app 模块的 'window-all-closed' 事件。如果用户不是在 macOS(darwin) 上运行程序,则调用 app.quit()。
      app.on('window-all-closed', () => {
        if (process.platform !== 'darwin') app.quit()
      })
      
    • 如果没有窗口打开则打开一个窗口 (macOS),macOS 应用通常即使在没有打开任何窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口。

      // 为了实现这一特性,监听 app 模块的 activate 事件。如果没有任何浏览器窗口是打开的,则调用 createWindow() 方法。
      // 因为窗口无法在 ready 事件前创建,你应当在你的应用初始化后仅监听 activate 事件。 通过在您现有的 whenReady() 回调中附上您的事件监听器来完成这个操作。
      
      app.on("ready", () => {
        createWindow();
      
        app.on('activate', () => {
          if (BrowserWindow.getAllWindows().length === 0) createWindow()
        })
      });
      
  • 完整项目结构
    在这里插入图片描述

  • 启动项目yarn start
    在这里插入图片描述

配置自动重启

  • 主进程每修改一次就得手动重启一次,可以配置自动重启

  • 安装nodemon

    • yarn add nodemon -D
      
  • 更改启动命令

    • {
        "scripts": {
          "start": " nodemon --exec electron ."
        },
      }
      
  • 更改完启动命令发现自动重启的是主进程,但不改主进程,只改页面,发现页面窗口是需要手动刷新的,配置一个nodemon.json即可实现页面文件也能自动刷新

    • {
        "ignore": ["node_modules", "dist"],
        "restartable": "r",
        "watch": ["*.*"],
        "ext": "html, css, js"
      }
      
      

主进程和渲染进程通信

  • 目前主进程和渲染进程是没有什么关联的,主进程只是配置了加载渲染进程,然后他们之前没有其他的关系了,此时如果需要在渲染进程里拿到主进程中的node版本号是不可行的,因为渲染进程是浏览器环境,没有process属性

  • 此时就需要一个预加载脚本作为主进程和渲染进程之间通信的桥梁,注意:预加载脚本可以拿到部分的node API,但不是所有

  • 主进程目录下创建preload.js

    • const { contextBridge } = require("electron");
      
      contextBridge.exposeInMainWorld("api", {
        version: process.version,
      });
      

在这里插入图片描述

  • 主进程需要配置运行预加载脚本

    • const createWindow = () => {
        const win = new BrowserWindow({
          width: 800,
          height: 600,
          webPreferences: {
            preload: path.resolve(__dirname, "./preload.js"),
          },
        });
        win.loadFile("./pages/index.html");
      };
      

在这里插入图片描述

  • 配置一下渲染进程的脚本

    • const nodeVersion = document.querySelector("#node-version");
      
      console.log(window);
      
      nodeVersion.textContent = api.version;
      
      
  • 最后结果
    在这里插入图片描述

  • 由此,引出,渲染进程跟主进程之间的相互通信,需要通过预加载脚本,因为预加载脚本也只能访问到node部分属性,像是文件操作等还是需要主进程来完成的

  • 完成在窗口输入内容,并写入文件,最近将文件读出来的需求即可完全理解进程间的通信
    在这里插入图片描述

  • main.js

    • const { app, BrowserWindow, ipcMain } = require("electron");
      const path = require("path");
      const fs = require("fs");
      
      const writeFile = (_, data) => {
        fs.writeFileSync(path.resolve(__dirname, "./hello.txt"), data);
      };
      
      const readFile = () => {
        return fs.readFileSync(path.resolve(__dirname, "./hello.txt")).toString();
      };
      
      const createWindow = () => {
        const win = new BrowserWindow({
          width: 800,
          height: 600,
          webPreferences: {
            preload: path.resolve(__dirname, "./preload.js"),
          },
        });
        ipcMain.on("write-file", writeFile);
        ipcMain.handle("read-file", readFile);
        win.loadFile("./pages/index.html");
      };
      
      app.on("ready", () => {
        createWindow();
      
        app.on("activate", () => {
          if (BrowserWindow.getAllWindows().length === 0) createWindow();
        });
      });
      
      app.on("window-all-closed", () => {
        if (process.platform !== "darwin") app.quit();
      });
      
      
  • preload.js

    • const { contextBridge, ipcRenderer } = require("electron");
      
      contextBridge.exposeInMainWorld("api", {
        version: process.version,
        writeFile: (data) => {
          ipcRenderer.send("write-file", data);
        },
        readFile: () => ipcRenderer.invoke("read-file"),
      });
      
      
  • render.js

    • const nodeVersion = document.querySelector("#node-version");
      const input = document.querySelector("#input");
      const write = document.querySelector("#write");
      const read = document.querySelector("#read");
      
      nodeVersion.textContent = api.version;
      
      window.onload = () => {
        write.addEventListener("click", () => {
          api.writeFile(input.value);
        });
      
        read.addEventListener("click", async () => {
          const text = await api.readFile();
          alert(text);
        });
      };
      
      
  • index.html

    • <!DOCTYPE html>
      <html lang="en">
        <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' 'unsafe-inline'"
          />
          <title>Hello World!</title>
          <link rel="stylesheet" href="./index.css" />
        </head>
        <body>
          <h1>Hello World!</h1>
          We are using Node.js <span id="node-version"></span>, Chromium
          <span id="chrome-version"></span>, and Electron
          <span id="electron-version"></span>.
          <hr />
          <input id="input" />
          <button id="write">写入文件</button>
          <button id="read">读取文件</button>
          <script src="./render.js"></script>
        </body>
      </html>
      
      

在这里插入图片描述

打包应用

  • 使用 electron build 打包成一个安装包应用

  • yarn add electron-build -D
    
  • electron-build配置地址:CodeBlog/Electron/electron-builder打包详解.md at master · QDMarkMan/CodeBlog (github.com)

  • 配置package.json

    • {
        "scripts": {
          "build": "electron-builder"
        },
        "build": {
          "appId": "com.electron.myapp",
          "nsis": {
            "oneClick": false,
            "allowElevation": true,
            "allowToChangeInstallationDirectory": true
          },
          "win": {
            "icon": "./vite.svg",
            "target": [
              {
                "target": "nsis",
                "arch": [
                  "x64"
                ]
              }
            ]
          }
        }
      }
      
  • electron-builder打包慢解决方法

    • electron-builder 打包太慢解决方法build过程耗时较长,可以手动下载部分文件。镜像地址 https:/ - 掘金 (juejin.cn)

http://www.kler.cn/news/359122.html

相关文章:

  • SPRINGBOOT 打包报错
  • YOLOv11改进策略【模型轻量化】| 替换骨干网络为 MobileViTv1高效的信息编码与融合模块,获取局部和全局信息
  • COALESCE 是 SQL 中的一个函数,用于返回第一个非 NULL 的表达式的值
  • 大数据都包括哪些内容
  • sass的使用
  • 单例设计模式(Singleton Pattern)
  • 数据分析-33-我国各地区近年来结婚离婚情况分析
  • Codeforces Round 979 (Div. 2) A-C 题解
  • 【Qt】详细Qt基础 (包括自定义控件)
  • 说说ConcurrentLinkedQueue的HOPS(延迟更新的策略)的设计?
  • 第二十六:TCP/IP的知识回顾
  • SpringCloudStream使用StreamBridge实现延时队列
  • 【C++打怪之路Lv9】-- vector
  • CMake变量:CMAKE_FIND_LIBRARY_SUFFIXES
  • 开关柜触头中的无线测温
  • DORA 机器人中间件学习教程(5)——3D激光雷达数据可视化
  • ATTCK 框架讲解
  • 线性代数 向量
  • 行业标准丨《变电站智能巡检导则:图像识别》(征求意见稿)
  • Scrapy | 使用Scrapy进行数据建模和请求