极简electron应用开发入门

avatar
FE @字节跳动
  1. 前言

本文通过从0开始编写一个electron小项目,使读者能够在短时间内了解electron项目的开发和基础概念

  1. 环境准备

nodeJs
使用淘宝镜像或者公司镜像安装electron
检查是否安装成功 官方demo npm install & npm start


  1. 项目初始化

项目通过create-react-app工具结合electron,新建一个react-electron项目

目录初始化

  1. 初始化一个react-app,并手动添加main.js文件
  2. 此时目录结构如下(preload.js 之后介绍)

image.png
3. 在package.json新增 "main": "./main.js"

image.png
4. 启动脚本里新增 "electron-start": "electron ."

image.png 5. 至此,目录准备完成

main.js(可以直接复制官方demo里的main)

const {app, ipcMain, BrowserWindow} = require('electron')
const debug = require('electron-debug')
const path = require('path')

function createWindow () {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload_render.js'),
      javascript: true,      // javascript Boolean (可选) - 是否启用 JavaScript 支持。 默认值为 true。
      nodeIntegration: true, // nodeIntegration Boolean (可选) - 是否启用Node integration. 默认值为 false.
      webSecurity: false,
      // 当设置为 false, 它将禁用同源策略 (通常用来测试网站), 如果此选项不是由开发者设置的,还会把 allowRunningInsecureContent设置为 true. 默认值为 true
      contextIsolation: false
      // 此项为true的话在App.js中无法通过window.require来引用electron包
      // Boolean (可选) - 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本. 默认为 true。
    }
  })

  mainWindow.loadURL('http://localhost:3000')
  debug.openDevTools(mainWindow)
}


ipcMain.on('asynchronous-message', (event, arg) => {
  console.log('main:', arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong' + arg)
})


ipcMain.handle('handle-message', async () => {
  const result = await Promise.resolve('123');
  return result;
})


app.whenReady().then(() => {
  createWindow()
  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})


app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

启动项目

npm run start 启动react项目
npm run electron-start 启动 electron main进程

然后就能看到我们本地的客户端加载了react页面

image.png

至此,项目初始化成功!

💥 hello world部分 到此结束

  1. electron基本概念介绍

主进程(main.js)

每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。

窗口进程

使用BrowserWindow创建的窗口

需要配置窗口大小、控制、Web相关配置等等

应用程序生命周期

主进程还能通过 Electron 的 app 模块来控制您应用程序的生命周期。

例如:初始化完成时 --> ready

关闭前 --> before-quit 等等

不同事件在MacWinodws上会有差异

原生api

API文档

渲染器进程(Renderer)

每个BrowserWindow有单独的render进程,通过ipcRenderer模块与主进程通信

预加载脚本(对应preload_render.js文件)

每个渲染进程优先于网页内容加载的脚本,有可以访问Node API,共享window,但不能向window中注入东西

  1. 主进程、渲染进程间通信

主进程(main.js中通信部分)

通过ipcMain进行监听,用2种方式举例 onhandle

const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
  console.log(arg) // prints "ping"
  event.reply('asynchronous-reply', 'pong')
})

ipcMain.handle('handle-message', async () => {
  const result = await Promise.resolve('123');
  return result;
})

渲染进程(App.js)(业务组件,一些数据与main进行通信)

在这里每次点击时给main进程发送一个消息,然后收到返回后更改view

import React, { useCallback, useEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
const { ipcRenderer } = window.require('electron')

const App = () => {
  const [clickTime, setClickTime] = useState(0);
  const [echo, setEcho] = useState('');
  const sendMsg = useCallback(() => {
    ipcRenderer.send('asynchronous-message', clickTime)
  }, [clickTime]);


  useEffect(() => {
    ipcRenderer.on('asynchronous-reply', (event, arg) => {
      console.log('renderer:', arg)
      setClickTime((clickTime) => clickTime += 1)
      setEcho(arg)
    })

    ipcRenderer.invoke('handle-message').then((res) => {
      console.log('handle-message:', res)
    })
  }, [])


  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p onClick={sendMsg}>
          click to send Msg
        </p>
        <p>
          echo : {echo}
        </p>
      </header>
    </div>
  );
}



export default App;

分别启动渲染进程和主进程

结果

完成App与Main之间的通信

main和renderer的消息打印

💥至此,简单的主、渲染进程间通信完成


  1. 项目打包,生成安装文件

安装打包工具electron-builder

  1. 首先npm run build打包App.js 生成build文件夹
  2. 改变main.js中引用的路径资源
// 原来 使用loadURL

mainWindow.loadURL('http://localhost:3000')

 

// 现在 使用loadFile加载文件

mainWindow.loadFile('./build/index.html')
  1. 执行 electron-builder --mac,需要在package中进行打包配置(或者单独配置json文件)

Windows 和 mac 配置有所区别

  "build": {
    "productName":"electronDemo",
    "appId": "tzz.electron",
    "copyright":"xxxx",
    "directories": {
      "output": "dist"
    },
    "asar": false,
    "mac": {
      "icon": "src/icon.png"
    },
    "extends": null,
     // 此处要写入要打包的文件,否则打包出来找不到对应文件
    "files": ["build/", "main.js", "package.json", "preload.js"]
  }
  1. 安装输出文件夹dist里的dmg文件,打开app
  2. 看到我们想要的结果

image.png

至此整个项目的开发就结束啦,可以正常在电脑上使用了