electron 起步

为什么要学 Electron,因为公司需要调试 electron 的应用。

Electron 是 nodechromium 的结合体,可以使用 JavaScript,HTML 和 CSS 等 Web 技术创建桌面应用程序,支持 Mac、Window 和 Linux 三个平台。

electron 的成功案例有许多,比如大名鼎鼎的 vscode

hello-world

官网有个快速启动的应用程序,我们将其下载到本地运行看一下。

# Clone this repository
git clone https://github.com/electron/electron-quick-start
# Go into the repository
cd electron-quick-start
# Install dependencies
npm install
# Run the app
npm start

:运行 npm i 时卡在> node install.js,许久后报错如下

$ npm i

> electron@20.1.0 postinstall electron\electron-quick-start\node_modules\electron
> node install.js RequestError: read ECONNRESET
at ClientRequest.<anonymous> (electron\electron-quick-start\node_modules\got\source\request-as-event-emitter.js:178:14)
at Object.onceWrapper (events.js:422:26)
at ClientRequest.emit (events.js:327:22)
at ClientRequest.origin.emit (electron\electron-quick-start\node_modules\@szmarczak\http-timer\source\index.js:37:11)
at TLSSocket.socketErrorListener (_http_client.js:432:9)
at TLSSocket.emit (events.js:315:20)
at emitErrorNT (internal/streams/destroy.js:84:8)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! electron@20.1.0 postinstall: `node install.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the electron@20.1.0 postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

网上搜索 postinstall: node install.js 的解决办法是将 electron 下载地址指向 taobao 镜像:

// 将electron下载地址指向taobao镜像
$ npm config set electron_mirror "https://npm.taobao.org/mirrors/electron/"

新建项目 electron-demo 并参考 electron-quick-start

// 新建文件夹
$ mkdir electron-demo
// 进入项目
$ cd electron-demo
// 初始化项目
$ npm init -y
// 安装依赖包
$ npm i -D electron

新建 electron 入口文件 index.js

// electorn-demo/index.js
const { app, BrowserWindow } = require('electron')
const path = require('path') function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
})
// 加载html页面
mainWindow.loadFile('index.html')
} app.whenReady().then(() => {
createWindow()
})

新建一个 html 页面(例如 index.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>Electron 网页</title>
</head>
<body>
hello world
</body>
</html>

在 package.json 中增加启动 electron 的命令:

{
"name": "electron-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 会运行 main 字段指向的 indx.js 文件
+ "start": "electron ."
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^20.1.1"
}
}

启动 electron 应用:

$ npm run start

Tip:windows 下通过 ctrl+shift+i 可以打开调试界面。或使用 mainWindow.webContents.openDevTools()也可以打开。

    // 加载html页面
mainWindow.loadFile('index.html')
// 默认打开调试工具
+ mainWindow.webContents.openDevTools()

热加载

现在非常不利于调试:修改 index.jsindex.html 需要新运行 npm run start 才能看到效果。

可以 electron-reloader 来解决这个问题。只要已修改代码,无需重启就能看到效果。

首先安装依赖包,然后修改 electron 入口文件即可:

$ npm i -D electron-reloader
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.3.2 (node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN electron-demo@1.0.0 No description
npm WARN electron-demo@1.0.0 No repository field. + electron-reloader@1.2.3
added 30 packages from 19 contributors and audited 109 packages in 26.217s 20 packages are looking for funding
run `npm fund` for details found 1 moderate severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details

electron 入口文件增加如下代码:

$ git diff index.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
// 参考 npmjs 包
+try {
+ require('electron-reloader')(module);
+} catch {}
+

重启服务后,再次修改 index.jsindex.html 等文件,只要保存就会自动看到效果。

主进程和渲染进程

官方api中有Main Process 模块Renderer Process 模块,比如我们在 hello-world 示例中使用的 BrowserWindow 标记了主进程,什么意思?

  • 主进程,通常是指 main.js 文件,是每个 Electron 应用的入口文件。控制着整个应用的生命周期,从打开到关闭。主进程负责创建 APP 的每一个渲染进程。一个 electron 应用有且只有一个主线程。
  • 渲染进程是应用中的浏览器窗口。 与主进程不同,渲染进程可能同时存在多个
  • 如果一个api同时属于两个进程,这里会归纳到 Main Process 模块
  • 如果在渲染进程中需要使用主进程的 api,需要通过 remote 的方式(下面会用到)。

菜单

通过 Menu.setApplicationMenu 新增菜单。代码如下:

// index.js
require('./menu.js')
// menu.js
const { BrowserWindow, Menu } = require('electron')
const template = [ {
id: '1', label: 'one',
submenu: [
{
label: '新开窗口',
click: () => {
const ArticlePage = new BrowserWindow({
width: 500,
height: 500,
})
ArticlePage.loadFile('article.html')
}
},
]
},
{ id: '2', label: 'two' },
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

效果如下图所示:

:新增窗口的菜单和主窗口的菜单相同。

自定义菜单

比如酷狗音乐,导航是比较好看。做法是隐藏原生菜单,用自己的 html 代替。

下面我们将原生菜单功能改为自定义菜单。

首先通过 frame 将应用的边框去除,原始菜单也不再可见:

const mainWindow = new BrowserWindow({
// frame boolean (可选) - 设置为 false 时可以创建一个无边框窗口 默认值为 true。
// 去除边框,菜单也不可见了
+ frame: false,
width: 1500,
height: 500,
})

Tip: frame boolean (可选) - 设置为 false 时可以创建一个无边框窗口 默认值为 true

接着在 html 页面实现自己的菜单。例如:

<p class="nav">
<span class="j-new">新建窗口</span>
<span>菜单2</span>
</p>

原始的导航是可以通过鼠标拖动。

我们可以使用 -webkit-app-region 来增加可拖拽效果:

<style>
.nav{-webkit-app-region: drag;}
.nav span{
-webkit-app-region: no-drag;
background-color:pink;
cursor: pointer;
}
</style>

:需要给菜单关闭拖拽效果,否则 cursor: pointer 会失效,点击也没反应,无法继续进行。

接下来给菜单添加事件,点击时打开新窗口。

这里得使用 BrowserWindow,则需要使用 require

直接使用 require 会报错 require is not defined。需要在主进程中开启:

const mainWindow = new BrowserWindow({
webPreferences: {
// 否则报错:require is not defined
+ nodeIntegration: true,
// 在新版本的electron中由于安全性的原因还需要设置 contextIsolation: false
+ contextIsolation: false,
},
})

接下来就得在引入 BrowserWindow,相当于在渲染进程中使用主进程的 api,需要使用 remote。如果这么用则会报错 const BrowserWindow = require("electron").remote.BrowserWindow,网友说:electron12 中已经废弃了remote 模块,如果需要则需自己安装 @electron/remote。

进入 npmjs 中搜索 @electron/remote,用于连接主进程到渲染进程的 js 对象:

// @electron/remote是Electron 模块,连接主进程到渲染进程的 js 对象

@electron/remote is an Electron module that bridges JavaScript objects from the main process to the renderer process. This lets you access main-process-only objects as if they were available in the renderer process.

// @electron/remote是electron内置远程模块的替代品,该模块已被弃用,最终将被删除。

@electron/remote is a replacement for the built-in remote module in Electron, which is deprecated and will eventually be removed.

用法如下:

// 安装依赖
$ npm install --save @electron/remote // 在主进程中进行初始化
require('@electron/remote/main').initialize()
require("@electron/remote/main").enable(mainWindow.webContents); // 渲染进程
const { BrowserWindow } = require('@electron/remote')

核心页面的完整代码如下:

  • 主进程页面:
// index.js 入口文件(主进程)

const { app, BrowserWindow} = require('electron')
// 在主进程中进行初始化,然后才能从渲染器中使用
require('@electron/remote/main').initialize() // 热加载
try {
require('electron-reloader')(module);
} catch { } function createWindow() {
const mainWindow = new BrowserWindow({ // 去除边框,菜单也不可见了
frame: false,
width: 1500,
height: 500,
webPreferences: {
// 否则报错:require is not defined
nodeIntegration: true,
// 在新版本的electron中由于安全性的原因还需要设置 contextIsolation: false
contextIsolation: false,
// 不需要 enableremoteModule,remote 也生效
// enableremoteModule: true
},
})
// 在electron >= 14.0.0中,您必须使用新的enableAPI 分别为每个所需的远程模块启用WebContents
// 笔者:"electron": "^20.1.1"
require("@electron/remote/main").enable(mainWindow.webContents); // 加载html页面
mainWindow.loadFile('index.html')
} app.whenReady().then(() => {
createWindow()
}) // 加载菜单
require('./menu.js')
  • 渲染进程页面
<!-- index.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>Electron 网页</title>
<style>
.nav{-webkit-app-region: drag;}
.nav span{-webkit-app-region: no-drag;background-color:pink;cursor: pointer;}
</style>
</head>
<body>
<p class="nav">
<span class="j-new">新建窗口</span>
<span><a href='www.baidu.com'>百度</a></span>
</p> hello world2
<script src="index-js.js"></script>
</body>
</html>
// index-js.js 渲染进程

// 渲染窗口使用 require 浏览器报错:Uncaught ReferenceError: require is not defined
const { BrowserWindow } = require('@electron/remote') const elem = document.querySelector('.j-new')
elem.onclick = function(){
const ArticlePage = new BrowserWindow({
width: 500,
height: 500,
})
ArticlePage.loadFile('article.html')
}

打开浏览器

现在需要点击自定义菜单中的百度,然后打开浏览器并跳转到百度。

这里主要用到 shell,它属于主进程也属于渲染进程,所以这里无需使用 remote 方式引入。首先在 index.html 中增加 a 标签,然后在 js 中注册点击事件,最后调用 shell.openExternal 即可。请看代码:

<p class="nav">
<span class="j-new">新建窗口</span>
<span><a href='https://www.baidu.com'>百度</a></span>
</p>
// index-js.js
const { shell} = require('electron')
// 渲染窗口使用 require 浏览器报错:Uncaught ReferenceError: require is not defined
const { BrowserWindow } = require('@electron/remote') ...
const aElem = document.querySelectorAll('a'); [...aElem].forEach(item => item.onclick=(e)=>{
// 防止主进程打开页面
e.preventDefault()
const url = e.target.getAttribute('href');
shell.openExternal(url)
})

点击百度,会打开本地默认浏览器。

Tip:如果不要 http 直接写成 www.baidu.com,笔者测试失败。

文件读取和保存

先看要实现的效果:

点击读取,选择文件后,文件内容会显示到 textarea 中,对 textarea 进行修改文案,点击保存,输入要保存的文件名即可保存。

这里需要使用主进程的 dialog api。由于 dialog 属于主进程,要在渲染进程中使用,则需要使用 remote。

dialog - 显示用于打开和保存文件、警报等的本机系统对话框。这里用到两个 api 分别用于打开读取文件和保存文件的系统窗口:

  • dialog.showOpenDialogSync,打开读取文件的系统窗口,可以定义标题、确定按钮的文本、指定可显示文件的数组类型等等,点击保存,返回用户选择的文件路径。接下来就得用 node 去根据这个路径读取文件内容
  • dialog.showSaveDialogSync,与读文件类似,这里是保存,返回要保存的文件路径

Tip:真正读取文件和保存文件还是需要使用 node 的 fs 模块。有关 node 的读写文件可以参考这里

核心代码如下:

// index-js.js
const fs = require('fs')
const { BrowserWindow, dialog } = require('@electron/remote')
... // 文件操作
const readElem = document.querySelector('.j-readFile')
const textarea = document.querySelector('textarea')
const writeElem = document.querySelector('.j-writeFile')
// 读文件
readElem.onclick = function () {
// 返回 string[] | undefined, 用户选择的文件路径,如果对话框被取消了 ,则返回undefined。
const paths = dialog.showOpenDialogSync({ title: '选择文件', buttonLabel: '自定义确定' })
console.log('paths', paths)
fs.readFile(paths[0], (err, data) => {
if (err) throw err;
textarea.value = data.toString()
console.log(data.toString());
});
} // 写文件
writeElem.onclick = function () {
// 返回 string | undefined, 用户选择的文件路径,如果对话框被取消了 ,则返回undefined。
const path = dialog.showSaveDialogSync({ title: '保存文件', buttonLabel: '自定义确定' })
console.log('path', path)
// 读取要保存的内容
const data = textarea.value
fs.writeFile(path, data, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});
}

注册快捷键

比如 vscode 有快捷键,这里我们也给加上。比如按 ctrl+m 时,最大化或取消最大化窗口。

这里需要使用 globalShortcut(主进程 ):在应用程序没有键盘焦点时,监听键盘事件。

用法很简单,直接注册即可。请看代码:

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

app.whenReady().then(() => {
const mainWindow = createWindow()
// 注册快捷键
globalShortcut.register('CommandOrControl+M', () => {
// 主进程会在 node 中输出,而非浏览器
console.log('CommandOrControl+M is pressed')
// 如果是不是最大,就最大化,否则取消最大化
!mainWindow.isMaximized() ? mainWindow.maximize() : mainWindow.unmaximize()
})
})

渲染进程中注册快捷键也类似,不过需要通过 remote 的方式引入。就像这样:

const { BrowserWindow, dialog, globalShortcut } = require('@electron/remote')

// 渲染进程中注册快捷键
globalShortcut.register('CommandOrControl+i', () => {
// 主进程会在 node 中输出,而非浏览器
console.log('CommandOrControl+i is pressed')
})

主线程和渲染进程通信

需求:在渲染进程中点击一个按钮,来触发主线程的放大或取消放大功能。

需要使用以下两个 api:

  • ipcMain 主进程 - 从主进程到渲染进程的异步通信。
  • ipcRenderer 渲染进程 - 从渲染器进程到主进程的异步通信。

Tip:在 Electron 中,进程使用 ipcMain 和 ipcRenderer 模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。

首先在主进程注册事件:

ipcMain.on('max-unmax-event', (evt, ...args) => {
// max-unmax-event。args= [ 'pjl' ]
console.log('max-unmax-event。args=', args)
})

接着在 index.html 中增加最大化/取消最大化按钮:

<p class="nav">
<span class="j-new">新建窗口</span>
+ <span class="j-max">最大化/取消最大化</span>
</p>

最后在渲染进程中,点击按钮时通过 ipcRenderer 触发事件,并可传递参数。就像这样

// 最大化/取消最大化
const maxElem = document.querySelector('.j-max')
maxElem.onclick = function() {
ipcRenderer.send('max-unmax-event', 'pjl')
console.log('max')
}

打包

打包可以借助 electron-packager 来完成。

// 安装
$ npm install --save-dev electron-packager // 用法
npx electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...] // 就像这样
"build": "electron-packager ./ electron-demo --platform=win32 --arch=x64 ./outApp --overwrite --icon=./favicon.ico"

笔者环境是 node13.14,构建失败:

$ npm run build

> electron-demo@1.0.0 build electron\electron-demo
> electron-packager ./ electron-demo --platform=win32 --arch=x64 ./outApp --overwrite --icon=./favicon.ico CANNOT RUN WITH NODE 13.14.0
Electron Packager requires Node >= 14.17.5.
npm ERR! code ELIFECYCLE
...

react/vue 中使用 electron

用法很简单,首先准备好 react 或 vue 项目,笔者就以多次使用的 react(spug) 项目来进行。

安装 electron 包:

$ npm i -D electron

在 package.json 中增加运行命令,指定 electron 入口文件:

$ git diff package.json
"name": "spug_web",
+ "main": "electron-main.js",
"scripts": {
"test": "react-app-rewired test",
"eject": "react-scripts eject",
+ "electron": "electron ."
},
"devDependencies": {
+ "electron": "^20.1.2",

编写 electron 入口文件如下,其中 loadFile 要改为 loadURL

// electron-main.js
const { app, BrowserWindow } = require('electron')
const path = require('path') function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
})
// loadFile 改为 loadURL
mainWindow.loadURL('http://localhost:3000/')
} app.whenReady().then(() => {
createWindow()
})
// 启动 react 项目,在浏览器中能通过 http://localhost:3000/ 正常访问
$ npm run start
// 启动 electron
$ npm run electron

正常的话, electron 中就能看到 react 的项目。效果如下:

Tip:如果发布的话,首先通过 react 构建,例如输出 dir,然后将 loadURL 改为 loadFile('./dir/index.html')

完整代码

index.js

// index.js 入口文件(主进程)

const { app, BrowserWindow, globalShortcut, ipcMain} = require('electron')
// 在主进程中进行初始化,然后才能从渲染器中使用
require('@electron/remote/main').initialize() // 热加载
try {
require('electron-reloader')(module);
} catch { } function createWindow() {
const mainWindow = new BrowserWindow({ // frame boolean (可选) - 设置为 false 时可以创建一个无边框窗口 默认值为 true。
// 去除边框,菜单也不可见了
frame: false,
width: 1500,
height: 500,
webPreferences: {
// 否则报错:require is not defined
nodeIntegration: true,
// 在新版本的electron中由于安全性的原因还需要设置 contextIsolation: false
contextIsolation: false,
// 不需要 enableremoteModule,remote 也生效
// enableremoteModule: true
},
})
// 在electron >= 14.0.0中,您必须使用新的enableAPI 分别为每个所需的远程模块启用WebContents
// 笔者:"electron": "^20.1.1"
require("@electron/remote/main").enable(mainWindow.webContents); // 加载html页面
mainWindow.loadFile('index.html')
return mainWindow
} app.whenReady().then(() => {
const mainWindow = createWindow()
// 注册快捷键
globalShortcut.register('CommandOrControl+M', () => {
// 主进程会在 node 中输出,而非浏览器
console.log('CommandOrControl+M is pressed')
// 如果是不是最大,就最大化,否则取消最大化
!mainWindow.isMaximized() ? mainWindow.maximize() : mainWindow.unmaximize()
})
}) // 加载菜单
require('./menu.js') ipcMain.on('max-unmax-event', (evt, ...args) => {
console.log('max-unmax-event。args=', args)
})

index.html

<!-- index.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>Electron 网页</title>
<style>
.nav{-webkit-app-region: drag;}
.nav span{-webkit-app-region: no-drag;background-color:pink;cursor: pointer;}
</style>
</head>
<body>
<p class="nav">
<span class="j-new">新建窗口</span>
<span><a href='https://www.baidu.com'>百度</a></span>
<span class="j-max">最大化/取消最大化</span>
</p> <p>hello world</p> <section>
<h3>文件操作</h3>
<textarea style="height:100px;width:100%;" placeholder="请先读取文件,修改后保存"></textarea>
<button class="j-readFile">读取文件</button>
<button class="j-writeFile">保存文件</button>
</section>
<script src="index-js.js"></script>
</body>
</html>

index-js.js

// index-js.js
const { shell, ipcRenderer } = require('electron')
const fs = require('fs')
// 渲染窗口使用 require 浏览器报错:Uncaught ReferenceError: require is not defined
const { BrowserWindow, dialog, globalShortcut } = require('@electron/remote') const elem = document.querySelector('.j-new')
elem.onclick = function () {
const ArticlePage = new BrowserWindow({
width: 500,
height: 500,
})
ArticlePage.loadFile('article.html')
} const aElem = document.querySelectorAll('a'); [...aElem].forEach(item => item.onclick = (e) => {
e.preventDefault()
const url = e.target.getAttribute('href');
console.log('href', url) shell.openExternal(url)
}) // 文件操作
const readElem = document.querySelector('.j-readFile')
const textarea = document.querySelector('textarea')
const writeElem = document.querySelector('.j-writeFile')
// 读文件
readElem.onclick = function () {
// 返回 string[] | undefined, 用户选择的文件路径,如果对话框被取消了 ,则返回undefined。
const paths = dialog.showOpenDialogSync({ title: '选择文件', buttonLabel: '自定义确定' })
console.log('paths', paths)
fs.readFile(paths[0], (err, data) => {
if (err) throw err;
textarea.value = data.toString()
console.log(data.toString());
});
} // 写文件
writeElem.onclick = function () {
// 返回 string | undefined, 用户选择的文件路径,如果对话框被取消了 ,则返回undefined。
const path = dialog.showSaveDialogSync({ title: '保存文件', buttonLabel: '自定义确定' })
console.log('path', path)
// 读取要保存的内容
const data = textarea.value
console.log('data', data)
fs.writeFile(path, data, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});
} // 渲染进程中注册快捷键
globalShortcut.register('CommandOrControl+i', () => {
// 主进程会在 node 中输出,而非浏览器
console.log('CommandOrControl+i is pressed')
// mainWindow.webContents.openDevTools()
// // 如果是不是最大,就最大化,否则取消最大化
// !mainWindow.isMaximized() ? mainWindow.maximize() : mainWindow.unmaximize()
}) // 最大化/取消最大化
const maxElem = document.querySelector('.j-max')
maxElem.onclick = function() {
ipcRenderer.send('max-unmax-event', 'pjl')
console.log('max')
}

menu.js

// menu.js
const { BrowserWindow, Menu } = require('electron')
const template = [ {
id: '1', label: 'one',
submenu: [
{
label: '新开窗口',
click: () => {
const ArticlePage = new BrowserWindow({
width: 500,
height: 500,
})
ArticlePage.loadFile('article.html')
}
},
]
},
{ id: '2', label: 'two' },
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

package.json

{
"name": "electron-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "electron .",
"build": "electron-packager ./ electron-demo --platform=win32 --arch=x64 ./outApp --overwrite --icon=./favicon.ico"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^20.1.1",
"electron-packager": "^16.0.0",
"electron-reloader": "^1.2.3"
},
"dependencies": {
"@electron/remote": "^2.0.8"
}
}

electron 起步的更多相关文章

  1. 初探Electron

    Electron是什么? 官网是这么描述的:Build cross platform desktop apps with JavaScript, HTML, and CSS 翻译一下:使用JavaSc ...

  2. electron-vue:Vue.js 开发 Electron 桌面应用

    相信很多同学都知道 Electron 可以帮助开发人员使用前端技术开发桌面客户端应用,今天介绍的 electron-vue 框架是一套基于 Vue.js 开发 Electron 桌面应用的脚手架,该项 ...

  3. 初探Electron,从入门到实践

    本文由葡萄城技术团队于博客园原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.   在开始之前,我想您一定会有这样的困惑:标题里的Electron ...

  4. 快速了解Electron:新一代基于Web的跨平台桌面技术

    本文引用了作者“ ConardLi”的<用JS开发跨平台桌面应用,从原理到实践>一文部分内容,原文链接:segmentfault.com/a/1190000019426512,感谢原作者的 ...

  5. 什么是electron

    Electron 是什么 定义 Electron是一个能让你使用传统前端技术(Nodejs, Javascript, HTML, CSS)开发一个跨平台桌面应用的框架.这里所说的桌面应用指的是在Win ...

  6. electron 应用开发优秀实践

    vivo 互联网前端团队-Yang Kun 一.背景 在团队中,我们因业务发展,需要用到桌面端技术,如离线可用.调用桌面系统能力.什么是桌面端开发?一句话概括就是:以 Windows .macOS 和 ...

  7. Electron使用与学习--(页面间的通信)

    目录结构: index.js是主进程js. const electron = require('electron') const app = electron.app const BrowserWin ...

  8. Electron使用与学习--(基本使用与菜单操作)

    对于electron是个新手,下面纯属个人理解.如有错误,欢迎指出.   一.安装 如果你本地按照github上的 # Install the `electron` command globally ...

  9. Electron 不完全快速手册

    Electron能干嘛 Vscode 基于Electron开发的,他是用来开发桌面软件的,可以轻易的跨平台 他的前身是atomshell,图标很丑,不用在意,一点也不像vscode也不用在意.   L ...

随机推荐

  1. C# · 委托语句简化演变

    1.委托基础语句形式 namespace QLVision { delegate void dHelp();//定义委托 static class Program { /// <summary& ...

  2. bat-winget-win平台的软件包管理器

    win10 1709版本以后 引入的包管理器,如果不可用 需要 更新一下 应用安装程序. winget命令的功能  常用的就  安装 卸载 更新  . 卸载 使用中如果提示 策略 不允许,可执行下面命 ...

  3. IDEA快速创建maven项目

    遇到问题不要急,不要怕. 一.  二. 三.  四.Finish进来之后,项目会加载一会,之后会是下面这样子.  五.继续往下面配置,建立java和resorces文件夹  六.下面配置tomcat服 ...

  4. 如果一个promise永不resolve,会内存泄漏吗

    答:跟内存泄漏没有直接关系gc的策略不会改变,如果该promise没有被人引用,就会被gc掉.如果仍被引用,就不会被gc掉.即使一个promise,resolve或者reject了,但是它还被人引用, ...

  5. spring-security 配置简介

    1.Spring Security 简介 Spring Security 是一个能够基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在 Spring 应用 ...

  6. IDEA的项目结构和IDEA的HelloWord

    IDEA首次驱动 1. 选择不导入任何设置,点击 OK 2. 选择 Create New Project 3. 点击 new 按钮,配置安装的 JDK9 版本 选择 JDK9 目录,点击确定 4. 不 ...

  7. 今天介绍一下自己的开源项目,一款以spring cloud alibaba为核心的微服务架构项目,为给企业与个人提供一个零开发基础的微服务架构。

    LaoCat-Spring-Cloud-Scaffold 一款以spring cloud alibab 为核心的微服务框架,主要目标为了提升自己的相关技术,也为了给企业与个人提供一个零开发基础的微服务 ...

  8. Wpf 多指应用开发解析

    1  首先分析多指事件与单指事件,以及执行顺序 2  事件阻断 订阅多指事件后,在TouchDown时 采用e.handle = true,阻断多指事件,或在ManipulationStarting. ...

  9. 树莓派4B串口测试与开发

    参考文档: https://shumeipai.nxez.com/2021/08/09/raspberry-pi-4-activating-additional-uart-ports.html 树莓派 ...

  10. NOI / 1.4编程基础之逻辑表达式与条件分支讲解-01:判断数正负

    总时间限制: 1000ms 内存限制: 65536kB 题目: 描述 给定一个整数N,判断其正负. 输入 一个整数N(-109 <= N <= 109) 输出 如果N > 0, 输出 ...