浅谈Electron,前端开发的第一个桌面应用

522 阅读3分钟

介绍

某些业务,需要获取用户登录的信息,从而帮忙用户自动完成其他的一系列的业务操作,从而简化用户操作。

根据需求:提出了两种方案,一种是开发google插件,另一种就是使用Electron。google插件的压缩包安装,居然是压死骆驼的稻草,所以Electron登场。

本篇文章不会讲解具体Electron开发,这边重点讲两个方面:一个是Electron运行机制,另一个讲解安装包的注意事项。

Electron运行机制

先看下下面的图:

image.png

主进程(Main Process): 是运行再nodejs上的集成了Chromium的运行平台。记住这个进程是在nodejs上运行的,所以没有window、alert等浏览器对象。

渲染进程(Renderer Process):是运行在Chromium中的,是渲染前端内容的。所以,这个进程是没有node的相关API和对象的,所以无法操作系统内容。

进程间通信

看下面的图:

image.png

Electron提供了两个对象【ipcMain】【ipcRenderer】,一个是主进程使用,一个是渲染进程使用。从而完成进程之间的通信。

主进程与渲染进程的桥梁

为啥要讲这个桥梁?假如,Electron加载了美团的官网,这个网站的代码不是我们能控制和接触到的,那么如何对网站上的Dom进行操作呢?这时就需要这个桥梁。这个桥梁让我们实现了对第三方网站的操作,同时又可以跟主进程进行通信。

  • 指定桥梁文件
const createWindow = () => {
  const win = new BrowserWindow({
    width: 1024,
    height: 768,
    autoHideMenuBar: true,
    webPreferences: {
      nodeIntegration: true, // 允许在渲染进程中使用Node.js
      preload: path.resolve(__dirname, './preload.js'), // 指定桥梁文件
    },
  })
}

记住,桥梁文件是执行在渲染进程中的,但是【nodeIntegration】配置成true,可以让桥梁程序能够使用部分的nodejs的API和对象。

  • 操作第三方网站的样例代码
// 主进程告知,页面加载完成
ipcRenderer.on('page-loaded', (event, data) => {
  console.log('page-loaded', data)

  // 为第三方网站添加操作
  if (window.location.href === 'https://xxxx.com/login') {
    var inputsPhone = document.querySelectorAll('input[placeholder="手机号码"]')
    console.log('load finish', inputsPhone.length)

    if (inputsPhone && inputsPhone.length > 0) {
      inputsPhone.forEach(function (input) {
        // 为input元素添加失焦事件监听器
        input.addEventListener('blur', function () {
          // 在这里编写失焦时想执行的代码
          localStorage.setItem('__root_action_mode', 'phone')
          localStorage.setItem('__root_action_phone', input.value)
        })
      })
    }
  }
})
// 当页面加载完成时的回调函数
win.webContents.on('did-finish-load', () => {
  setTimeout(() => {
    win.webContents.send('page-loaded', 'finish-load')
  }, 1000)
})

Electron 应用打包

使用的打包插件是【electron-builder】,样例配置如下:

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "./code/main.js",
  "scripts": {
    "start": "electron .",
    "rollup": "rollup --config rollup.config.js",
    "build": "electron-builder"
  },
  "build": {
    "appId": "com.XXX.XXXX",
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": ["x64"]
        }
      ]
    },
    "nsis": {
      "oneClick": false,
      "perMachine": true,
      "allowToChangeInstallationDirectory": true
    },
    "files": ["code/*", "node_modules/**/*"]
  },
  "author": "XXXXX",
  "license": "ISC",
  "packageManager": "pnpm@8.6.2",
  "volta": {
    "node": "20.16.0"
  },
  "devDependencies": {
    "electron": "^33.2.1",
    "electron-builder": "^25.1.8",
    "rollup": "^2.79.2",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-copy": "^3.5.0"
  },
  "dependencies": {
    "axios": "^1.7.8"
  }
}

需要注意的是,需要使用【rollup】对代码进行混淆,然后,修改【"main": "./code/main.js"】的入口文件的指向。其次,【files】来配置打包执行的相关文件,排除无需打包的文件。

补充rollup打包配置

// rollup.config.js
import { terser } from 'rollup-plugin-terser'

const mainConfig = {
  input: './main.js',
  output: {
    file: 'code/main.js',
    format: 'iife',
  },
  plugins: [
    terser({
      compress: true,
      mangle: true,
      output: {
        comments: false,
      },
    }),
  ],
}

const preloadConfig = {
  input: 'preload.js',
  output: {
    file: 'code/preload.js',
    format: 'iife',
  },
  plugins: [
    terser({
      compress: true,
      mangle: true,
      output: {
        comments: false,
      },
    }),
  ],
}

export default [mainConfig, preloadConfig] // 导出配置数组

注意

  • 执行打包命令时,需要从github上下载4~5个文件,所以,如果没有梯子,则打包过程会非常痛苦。所以,可以根据网上的教程,实现离线打包。