Lei’s
课程博客讨论LIVE
登录立即订阅
Electron 跨平台桌面应用开发

Electron 跨平台桌面应用开发

6 年前/教程/- 阅读

Electron 跨平台桌面应用开发

Electron 是一个运行平台,它能够让我们通过 HTML + CSS + JavaScript 开发桌面应用程序。

核心原理就是 Electron 中将 Chromium(Chrome 的内核)和 Node.js 打包到了一起,通过 Chromium 提供 WebView 从而实现 UI 编程能力,通过 Node.js 提供 APIs 从而实现系统接口调用。

Electron 运行环境结构

简单来说就是,在 Electron 中我们可以像在 Web 开发中一样,通过 HTML + CSS 完成 UI 开发,通过 JavaScript(ECMAScript)调用 APIs 实现业务功能,只不过这里的 APIs = Web APIs + Node APIs。

快速上手

一个最基本的 Electron 项目需要有如下的几个必要文件:

your-app/
├── index.html
├── main.js
└── package.json

与 Web 所不同的是,这里 Electron 启动的入口是 JavaScript 文件,也就是这里的 main.js 文件(简单示例)。

// main.js
const { app, BrowserWindow } = require('electron')

app.on('ready', () => {
  // 创建浏览器窗口
  const win = new BrowserWindow({
    width: 800,
    height: 600
  })

  // 加载 index.html 文件
  win.loadFile('index.html')
})

在这个 JavaScript 文件中创建页面窗口用于加载所需要在界面上显示的页面文件 index.html。更完整的 main.js 应该如下:

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

// 保持对 window 对象的全局引用,如果不这么做的话,
// 当 JavaScript 对象被垃圾回收的时候,
// window 对象对应的窗口将会自动被关闭
let win

const createWindow = () => {
  // 创建浏览器窗口。
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // 加载 index.html 文件
  win.loadFile('index.html')

  // 打开开发者工具
  win.webContents.openDevTools()

  // 当 window 被关闭,这个事件会被触发。
  win.on('closed', () => {
    // 取消引用 window 对象,如果你的应用支持多窗口的话,
    // 通常会把多个 window 对象存放在一个数组里面,
    // 与此同时,你应该删除相应的元素。
    win = null
  })
}

// Electron 会在初始化后并准备创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow)

// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
  // 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
  // 否则绝大部分应用及其菜单栏会保持激活。
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // 在 macOS 上,当单击 dock 图标并且没有其他窗口打开时,
  // 通常在应用程序中重新创建一个窗口。
  if (win === null) {
    createWindow()
  }
})

// 在这个文件中,你可以续写应用剩下主进程代码。
// 也可以拆分成几个文件,然后用 require 导入。

index.html 中可以是任何你想要呈现的内容。你也可以使用不同的库和框架,这与 Web 中没什么两样,所不同的是,**当你 BrowserWindow → webPreferences → nodeIntegration 设置为 true 时,你可以在页面的脚本中使用 Node APIs。**这看起来非常棒,但是要小心使用这个特性,因为如果你加载的不是本地的脚本,那就存在风险。

案例:文件编辑器

为了体现 Electron 的能力,我们通过一个简单的记事本应用案例来感受。

克隆基础项目结构代码:

$ git clone https://github.com/electron/electron-quick-start.git onetext --depth 1
$ cd onetext
$ rm -rf .git

安装项目依赖的模块:

$ yarn # or npm install

页面结构与样式:

<!-- index.html -->
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Editor</title>
    <style>
      html,
      body {
        margin: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
      }

      #editor {
        box-sizing: border-box;
        padding: 3%;
        width: 100%;
        height: 100%;
        border: 1px solid #ddd;
        outline: 0;
        font-size: 14px;
        resize: none;
      }
    </style>
  </head>
  <body>
    <textarea id="editor" autofocus></textarea>
    <script src="./renderer.js"></script>
  </body>
</html>

页面脚本文件 renderer.js:

// renderer.js
const os = require('os')
const fs = require('fs')
const path = require('path')

const editor = document.getElementById('editor')

const filename = path.join(os.homedir(), 'foo.txt')

const save = () => {
  fs.writeFileSync(filename, editor.value)
}

const open = () => {
  editor.value = fs.readFileSync(filename, 'utf8')
}

document.addEventListener('keydown', e => {
  if (e.ctrlKey && e.keyCode === 83) {
    save()
    return false
  }
  if (e.ctrlKey && e.keyCode === 79) {
    open()
    return false
  }
})

当然 Electron 模块中提供了更合适的 APIs,用于提示用户保存文件位置、让用户选择打开某个文件:

const fs = require('fs')
const { dialog } = require('electron').remote

const editor = document.getElementById('editor')

const save = () => {
  const filename = dialog.showSaveDialogSync()
  filename && fs.writeFileSync(filename, editor.value)
}

const open = () => {
  const result = dialog.showOpenDialogSync({ properties: ['openFile'] })
  if (!result) return
  const filename = result[0]
  editor.value = fs.readFileSync(filename, 'utf8')
}

document.addEventListener('keydown', e => {
  if (e.ctrlKey && e.keyCode === 83) {
    save()
    return false
  }
  if (e.ctrlKey && e.keyCode === 79) {
    open()
    return false
  }
})

注意事项:

  1. 在渲染进程中使用 Node APIs 需要开启当前 BrowserWindow 的 nodeIntegration(针对 Electron 5 以上版本)。
  2. 由于渲染进程是运行在 Chromium 中的,所以不管是样式还是脚本都不需要考虑兼容其他环境问题,你可以放心大胆的使用新特性。

通过以上的示例,我们应该就可以体会到 Electron 开发的过程,以及 Electron 的内部组成。

主进程与渲染进程

Electron 中有两种进程类型,分别为「主进程」和「渲染进程」,它们的职责和能力各不相同:

通过 Electron 直接启动运行的脚本,运行这个脚本的进程被称为「主进程」。 一个 Electron 应用总是有且只有一个主进程。一般我们都会在这个进程中管理整个应用,所以我个人也把它称之为「调度进程」。

通过 BrowserWindow 创建的页面窗口运行在单独的进程当中,称之为「渲染进程」。负责展示页面以及运行页面上所需要的脚本。

主进程与渲染进程

With Framework

接下来我们再来了解一下,如何配合使用 React 或者 Vue.js 这样的 UI 框架开发 Electron 应用。

如果你不需要使用 JSX 或者单文件组件这些特性,你可以直接把这些框架当作库,直接在页面中使用。

当然,就现阶段来说,大家很自然的会把这些有特性和框架捆绑,总是用 A 就必须要用 B,所以这里还是需要推荐给大家一个我觉得非常方便的集成环境。

electron-webpack

electron-webpack 其实是一个通过 webpack 编译 Electron 代码的集成工具,通过简单的配置就可以支持 React 和 Vue.js,当然你也可以让它支持更多。

它要求你有通过特定的项目结构编写代码:

my-project/
├─ src/
│  ├─ main/
│  │  └─ index.js
│  ├─ renderer/
│  │  └─ index.js
│  └─ common/
└─ static/

我们可通过官方提供的模板快速创建这样结构的项目:

$ git clone https://github.com/electron-userland/electron-webpack-quick-start.git my-project --depth 1
$ cd my-project
$ rm -rf .git

# install dependencies
$ yarn # or npm install

这个项目中提供了一些有用的 scripts:

# run application in development mode
yarn dev

# compile source code and create webpack output
yarn compile

# `yarn compile` & create build with electron-builder
yarn dist

# `yarn compile` & create unpacked build with electron-builder
yarn dist:dir

使用 Vue.js

由于 electron-webpack 中会自动加载 Vue.js 所需的 loader,所以只需要安装对应的模块,Vue.js 单文件组件将自动工作。

yarn add vue electron-webpack-vue --dev

使用 React

同理,React 的工作也只需要安装相应的模块:

yarn add react react-dom @babel/preset-react --dev

打包和发布

我们可以使用类似 electron-builder 的一些集成工具轻松完成跨平台打包任务。

刚刚介绍的 electron-webpack 的初始项目模板中就包含了此工具的使用。

我最近在重写一个基于 Vue.js + TypeScript 的 Electron 骨架项目,如需自取:

https://github.com/zce/electron-boilerplate

文章信息及许可

本文采用《CC BY-SA 4.0 约定》进行许可

#Electron
汪磊汪

汪磊

MAKE IT BETTER!

查看更多

讨论区

相关内容推荐

快速掌握 AJAX - 基础
教程 · 7 年前 · 11 min

快速掌握 AJAX - 基础

背景介绍 在了解 AJAX 之前我们可以简单的认为「JavaScript 能力有限」,因为在此之前 Web 平台提供所有的 API 都只停留在「单机」的阶段。 这样就会造成一些无法实现的功能,例如: 无法在实现用户登录功能时,当用户输入邮箱地址显示用户对应的头像 无法在实现用户注册功能时,当用户输入邮箱或者用户名就提示是否存在 无法在实现留言板功能时,实时看到最新的用户留言 思考:为什么做不到这些呢? 这些功能的开发最终都卡在一个相同的问题上:所需要的数据存放在服务端,我们无法通过已知的 API 获取到服

汪磊汪继续阅读

教程

  • 前端构建新玩法:Vite 上手与思考
  • 使用 Parcel 零配置完成应用打包构建任务
  • Webpack 与 Rollup 二者之间该如何选择?
  • 生产环境下 Webpack 构建结果该如何优化?
查看更多文章
前端工程化 - 概述
教程 · 6 年前 · 3 min

前端工程化 - 概述

前端工程化是指遵循一定的标准和规范,通过工具提高开发效率、降低维护成本的一种手段。近些年被广泛的关注和探讨,究其原因,主要是因为前端应用的功能要求不断提高,业务逻辑日益复杂。 作为当下互联网时代唯一不可或缺的技术,前端占据了整个开发行业的「半壁江山」。从传统的网站到现在的 H5、移动 App、桌面应用以及小程序,前端技术几乎无所不能的全面覆盖。 在这些表象的背后实际上是前端行业对开发人员的技能要求发生了天翻地覆的变化,以往“写 Demo,套模板,调页面”这种刀耕火种的方式已经完全不符合当下对开发效率的要求。前端工程

汪磊汪继续阅读

社交媒体

我们会将最新的、最有意思的内容直接发送到您的收件箱。

标签

  • #工程化,
  • #模块化,
  • #Webpack,
  • #ES6,
  • #轻应用,
  • #AJAX,
  • #Electron,
  • #Rollup,
  • #TypeScript,
  • #自动化,
  • #脚手架,
  • #快应用,
  • #小程序,
  • #Loader,
  • #Plugin,
  • #Principle,
  • #Efficiency,
  • #SourceMap,
  • #HMR,
  • #Tree-shaking,
  • #CodeSplitting,
  • #Optimization,
  • 更多 »

链接

  • 故事
  • 关于我
  • 联系我们
  • 全部分类
  • 内容归档
  • 实验室

订阅

ITBetter
</> with by zce© 2025 Lei’s. 保留所有权利.京ICP备14041905号隐私政策服务条款

讨论区

0 条评论

    加入讨论

    注册成为我们的会员,加入我们的讨论

    加入我们

    已经是会员了? 立即登录