您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
入门Electron,手把手教你编写完整实用案例
 
 
  145  次浏览      7 次
2024-12-23
 
编辑推荐:
本文主要介绍了 Electron,包括其简介、工作机制、任务管理案例开发流程等。希望对您的学习有所帮助。
本文来自于稀土掘金,由火龙果软件Linda编辑、推荐。

文章主要介绍了 Electron,包括其简介、工作机制、任务管理案例开发流程等。Electron 用 JavaScript、HTML 和 CSS 构建跨平台桌面应用,区分主进程和渲染进程。文中通过案例讲解了初始化项目、调试、IPC 通信、窗口位置设定、关闭窗口等知识,最后提及下篇将探讨打包问题。

Electron简介

Electron是干什么的? 简单来讲,Electron 使用 JavaScript,HTML 和 CSS,来构建跨平台的桌面应用程序。

按照官方的说法:如果你可以建一个网站,你就可以建一个桌面应用程序。

和传统的桌面应用相比,使用Electron开发更容易上手,开发效率更高。并且,web技术应用广泛、生态繁荣,Electron可以使用几乎所有的Web生态领域及Node.js生态领域的组件和技术方案。

与网页应用相比,Electron基于Chromium 和 Node.js,可以避免令人头痛的浏览器兼容问题。而Web前端受限访问的文件系统、系统托盘、系统通知等,开发Electron应用时可以自由地使用。

简单理解Electron工作机制

使用Electron开发的桌面应用,类似于简易版的、定制版的Chrome浏览器,当然这个浏览器中的页面不能通过输入网址打开,而是由开发者写好的。

和浏览器架构类似,Electron应用程序区分主进程和渲染进程。

主进程负责控制应用程序的生命周期、创建和管理应用程序窗口,有着多种控制原生桌面功能的模块,例如菜单、对话框以及托盘图标。

渲染进程负责完成渲染界面、接收用户输入、响应用户的交互等工作。

一个Electron应用只有一个主进程,但可以有多个渲染进程。

在之前的文章中,我们有讲过浏览器中的进程,可略作参考。

案例入门

下面我们将从一个任务管理的案例入门,了解electron的整体开发流程和一些基本的细节知识。

案例效果

功能分析:

1、记录待完成任务和已完成任务

2、通过新建,添加待完成任务,并设置时间

3、点击完成任务,跳转到已完成界面;点击删除,可以删除任务

4、点击右上角的 × 按钮,可以关闭主界面,要再次打开主界面,可以通过系统托盘

5、设定的时间到了,会在右下角弹出提醒框,如下图所示。

初始化项目

项目是由原生js开发,在后面的文章中,我们会再探讨electron和vue、react这些前端框架的结合。

mkdir tasky
cd tasky
npm init

安装electron

npm install electron --S

 

创建一个hello world应用程序

(1)第一步,在项目根目录下,创建index.js,作为应用程序的入口文件。因为Electron是基于Node.js,所以入口文件使用Node.js语法。内容如下:


//引入两个模块:app 和 BrowserWindow

//app 模块,控制整个应用程序的事件生命周期。
//BrowserWindow 模块,它创建和管理程序的窗口。

const { app, BrowserWindow } = require('electron')

//在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口
app.on('ready', () => {

//创建一个窗口
const mainWindow = new BrowserWindow()

//窗口加载html文件
mainWindow.loadFile('./src/main.html')
})

 

(2)第二步,创建窗口需要加载的html文件。

为了方便后面的文件管理,我们新建一个 src 文件夹,用于存放web页面资源,比如html、css、js、图片等。

// ./src/main.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">
    <title>Document</title>
</head>
<body>
    hello world
</body>
</html>

(3)启动程序。

修改package.json的scripts,如下:

然后在项目根目录运行:npm run start。

这样我们的hello world应用程序就跑起来了。入门Electron,就是这么简单!

简单的基础调试

1、主进程运行时的一些提示信息会在命令行显示,比如,在index.js加入console.log

app.on('ready', () => {
    console.log('just test console.log')
   const mainWindow = new BrowserWindow()
   mainWindow.loadFile('./src/main.html')
})

就可以在命令行看到打印的值:

当index.js中的内容发生改变,默认要手动重启,比较麻烦。这里我们加入nodemon,它可以监控node.js 源代码的变化,并自动重启应用。

安装: npm i nodemon --D

修改scripts:"start": "nodemon --watch index.js --exec electron ."

再次运行npm run start,当index.js内容变化时,就会自动重新执行electron .来重启应用。

2、窗口页面的调试方法和chrome浏览器类似。点击菜单栏的View --- Toggle Developer Tools,或者按它对应的快捷键,就会出现我们熟悉的开发者工具界面。

当页面内容发生变化,可以点击View --- Reload,或者快捷键ctrl+r,刷新页面内容。页面热更新会后续讲到。

开始coding

项目目录结构如下,其中src文件夹存放的就是web页面相关的内容。

项目有两个窗口:主窗口和提醒窗口。在主窗口中管理任务,当任务设定时间到,会在屏幕右下角出现提醒窗口。

应用不涉及服务端数据,任务数据主要使用localStorage来存储。

创建主窗口:


const iconPath = path.join(__dirname, './src/img/icon.png') //应用运行时的标题栏图标
let mainWindow
app.on('ready', () => {
    mainWindow = new BrowserWindow({
       resizable: false, //不允许用户改变窗口大小
       width: 800, //设置窗口宽高
       height: 600,
       icon: iconPath, //应用运行时的标题栏图标
       webPreferences:{
          backgroundThrottling: false, //设置应用在后台正常运行
          nodeIntegration:true, //设置能在页面使用nodejs的API
         contextIsolation: false,
         preload: path.join(__dirname, './preload.js')
       }
    })
mainWindow.loadURL(`file://${__dirname}/src/main.html`)
}

main.html的内容很简单,有兴趣的童鞋可以去看源码,这里就不贴了。

默认Electron应用顶部有标题栏和菜单栏。纵观各个桌面应用程序,基本都是定制的顶部控制区域。对于我们这个应用,暂时只要一个关闭按钮,所以我们将去掉这一部分,将窗口关闭按钮写在main.html页面中。

无边框窗口

要创建无边框窗口,只需在 BrowserWindow 的 options 中将 frame 设置为 false:

mainWindow = new BrowserWindow({
    frame: false,
    ...
})

标题栏和菜单栏消失了,但也会有几个问题:

1、虽然菜单栏消失了,但是依然可以通过快捷键进行菜单操作,比如ctrl+shift+i打开开发者工具,为避免这种情况,我们需要去掉菜单栏:

mainWindow.removeMenu()

2、默认情况下,无边框窗口是不可拖拽的。应用程序需要在 CSS 中指定 -webkit-app-region: drag 来告诉 Electron 哪些区域是可拖拽的。


html,body {
    height: 100%;
    width: 100%;
}

body{
    -webkit-app-region: drag;
}

如果用上面的属性使整个窗口都可拖拽,则必须将其中的按钮标记为不可拖拽,否则按钮将无法点击。

.enable-click {
    -webkit-app-region: no-drag;
}

3、当点击自定义的窗口关闭按钮,我们并不希望退出程序,只是将窗口隐藏,可以通过系统托盘再次打开窗口。

系统托盘

程序启动时,将应用程序加入系统托盘。在Electron中,借助Tray模块实现。

const { app, BrowserWindow, Tray, Menu } = electron
const iconPath = path.join(__dirname, './src/img/icon.png')
let mainWindow, tray
app.on('ready', () => {
    mainWindow = new BrowserWindow({
      //... options
    })
   mainWindow.loadURL(`file://${__dirname}/src/main.html`)

   tray = new Tray(iconPath) //实例化一个tray对象,构造函数的唯一参数是需要在托盘中显示的图标url

   tray.setToolTip('Tasky') //鼠标移到托盘中应用程序的图标上时,显示的文本

    tray.on('click', () => { //点击图标的响应事件,这里是切换主窗口的显示和隐藏
       if(mainWindow.isVisible()){
          mainWindow.hide()
       }else{
          mainWindow.show()
       }
   })

    tray.on('right-click', () => { //右键点击图标时,出现的菜单,通过Menu.buildFromTemplate定制,这里只包含退出程序的选项。
       const menuConfig = Menu.buildFromTemplate([
       {
          label: 'Quit',
          click: () => app.quit()
       }
       ])
       tray.popUpContextMenu(menuConfig)
    })

})

 

IPC通信

回到上一个问题。点击页面内的按钮怎样隐藏窗口?这就需要用到IPC通信了。

IPC(Inter-Process Communication),就是进程间通信。Electron应用程序区分主进程和渲染进程,有时候,两者之间需要通信,传输一些数据、发送一些消息。

渲染进程 TO 主进程

比如,点击关闭按钮,就需要渲染进程向主进程发送隐藏主窗口的请求。

渲染进程使用Electron内置的ipcRenderer模块向主进程发送消息,ipcRenderer.send方法的第一个参数是消息管道名称。


//页面的js代码:
const electron = require('electron')
const { ipcRenderer } = electron

closeDom.addEventListener('click', () => {
    ipcRenderer.send('mainWindow:close')
})

主进程通过ipcMain接收消息,ipcMain.on方法的第一个参数也为消息管道的名称,与ipcRenderer.send的名称对应,第二个参数是接收到消息的回调函数。


//入口文件index.js
ipcMain.on('mainWindow:close', () => {
    mainWindow.hide()
})

主进程 TO 渲染进程

主进程向渲染进程发送消息是通过渲染进程的webContents。在mainWindow渲染进程设定了任务后,会传输给主进程任务信息,当任务时间到了,主进程会创建提醒窗口remindWindow,并通过remindWindow.webContents将任务名称发给remindWindow。


function createRemindWindow (task) {

    remindWindow = new BrowserWindow({
       //options
    })
    remindWindow.loadURL(`file://${__dirname}/src/remind.html`)

    //主进程发送消息给渲染进程
    remindWindow.webContents.send('setTask', task)

}

在remindWindow渲染进程中,通过ipcRenderer.on接受消息。


ipcRenderer.on('setTask', (event,task) => {
   document.querySelector('.reminder').innerHTML =
      `<span>${decodeURIComponent(task)}</span>的时间到啦!`
})

渲染进程 TO 渲染进程

渲染进程之间传递消息,可以通过主进程中转,即窗口A先把消息发送给主进程,主进程再把这个消息发送给窗口B,这种非常常见。

也可以从窗口A直接发消息给窗口B,前提是窗口A知道窗口B的webContents的id。

ipcRenderer.sendTo(webContentsId, channel, ...args)

值得注意的是,我们在页面的js代码中使用了require,这也是Electron的一大特点,在渲染进程中可以访问Node.js API。这样做的前提是在创建窗口时配置webPreferences的nodeIntegration: true和contextIsolation: false:


mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences:{
       nodeIntegration: true,
       contextIsolation: false
    }
})

窗口位置

当任务时间到,提醒窗口会在屏幕右下角出现。怎样设定窗口位置呢?

function createRemindWindow (task) {
    //创建提醒窗口
    remindWindow = new BrowserWindow({
       //...options
    })

    //获取屏幕尺寸
    const size = screen.getPrimaryDisplay().workAreaSize

    //获取托盘位置的y坐标(windows在右下角,Mac在右上角)
    const { y } = tray.getBounds()

    //获取窗口的宽高
    const { height, width } = remindWindow.getBounds()

    //计算窗口的y坐标
    const yPosition = process.platform === 'darwin' ? y : y - height

    //setBounds设置窗口的位置
    remindWindow.setBounds({
    x: size.width - width, //x坐标为屏幕宽度 - 窗口宽度
    y: yPosition,
    height,
    width
})

    //当有多个应用时,提醒窗口始终处于最上层
    remindWindow.setAlwaysOnTop(true)

    remindWindow.loadURL(`file://${__dirname}/src/remind.html`)
}

 

关闭窗口

提醒窗口会在一段时间后关闭,可以通过remindWindow.close()来关闭窗口。

当窗口关闭后,我们可以设置remindWindow = null来回收分配给该渲染进程的资源。

remindWindow.on('closed', () => { remindWindow = null })

结语

这篇文章主要是介绍electron的一些基础知识,下一篇文章,我们将探讨electron的打包问题,下次见。

凡能用JavaScript实现的,注定会被用JavaScript实现。

               --Jeff Atwood

 

   
145 次浏览       7
相关文章

整洁架构在前端的设计思想与应用实践
细说 Vue 响应式原理的 10 个细节!
前端性能精进之浏览器——呈现
从输入URL到Web页面呈现的全过程
相关文档

浅谈 Vue 3 响应式与组合式 API
35分钟掌握Angular核心概念
java-web-使用分层实现业务处理
HTML5的canvas绘图
相关课程

HTML 5 + CSS3 原理与开发应用
Web前端高级工程师必备技能实战
使用Vue.jS构建web应用实战
Vue大型项目开发实战

最新活动计划
C++高级编程 12-25 [线上]
白盒测试技术与工具实践 12-24[线上]
LLM大模型应用与项目构建 12-26[特惠]
需求分析最佳实践与沙盘演练 1-6[线上]
SysML建模专家 1-16[北京]
UAF架构体系与实践 1-22[北京]
 
 
最新文章
整洁架构在前端的设计思想与应用实践
细说 Vue 响应式原理的 10 个细节!
前端性能精进之浏览器——呈现
VUE.JS组件化开发实践
深入理解JSCore
最新课程
HTML 5 + CSS3 原理与开发应用
Web前端高级工程师必备技能实战
Vue大型项目开发实战
React原理与实践
Vue.js进阶与案例实践
成功案例
中交集团 构建Web自动化测试框架
某著名电信公司 Vue.js进阶与案例
国电通网络技术 HTML5+CSS3 +web前端框
移动通信 移动互联网应用开发原理
某电力行 android开发平台最佳