[TOC]
# 安装
```
ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/" npm install // mac 命令行下
```
> [tutorial/installation](https://www.electronjs.org/docs/tutorial/installation)
# Electron 架构
![](https://img.kancloud.cn/4d/ca/4dcadc9c8d5e7ecba05361beef75d577_1760x1360.png)
![](https://img.kancloud.cn/d3/2d/d32d17855568aaf0a5ea059461bd26c4_689x768.png)
> [Electron:PC 端多端融合方案](https://segmentfault.com/a/1190000022543101)
# 基础
一个 Electron 应用总是有且只有一个主进程。
每个`electron`中的 web 页面 运行在它自己的渲染进程中
每个`BrowserWindow`实例都在自己的渲染进程里运行页面
nodejs 在主进程和渲染进程中都可以使用。渲染进程因为安全限制,不能直接操作原生 GUI。
# 右键菜单
~~~
/*
*给窗口加事件
*/
window.addEventListener('contextmenu', (e) => {
e.preventDefault(); //阻止默认事件
var rightM = Menu.buildFromTemplate([
{label:'返回'},
{label:'前进'},
{label:'重新加载'},
{type:'separator'},
{label:'存储为...'},
{label:'打印...'},
{label:'投射...'},
{label:'翻译成中文(简体)'},
{type:'separator'},
{label:'查看网页源代码'},
{label:'检查'},
]);
rightM.popup({window: remote.getCurrentWindow()});
}, false);
~~~
# 主进程
## session
管理浏览器会话,Cookie,缓存,代理设置等。
## webContents
`webContents`是一个[EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)。
它负责渲染并控制BrowserWindow实例的内容,并且是该`BrowserWindow`对象的属性。
## contentTracing
从Chromium的内容模块收集跟踪数据,以查找性能瓶颈和缓慢的操作。
# 渲染进程模块
主进程模块就不介绍了,渲染模块之外或者部分都在主进程模块可用~
## shell
用来操作本地文件
## clipboard
## crashReporter
将崩溃报告提交给远程服务器,要搭建专用的服务器
~~~javascript
const {crashReporter} = require('electron')
crashReporter.start({
productName: 'YourName',
companyName: 'YourCompany',
submitURL: 'https://your-domain.com/url-to-submit',
uploadToServer: true
})
~~~
> [tencent-Electron 教程](https://cloud.tencent.com/developer/section/1116147)
## nativeImage
使用 PNG 或 JPG 文件创建托盘,底座和应用程序图标。
## ipcRenderer
渲染进程 ipc 通信的模块
## remote
可以调用 main 进程对象的方法, 而不必显式发送进程间消息。
1. 给主进程传递回调函数
2. 访问主进程中的内置模块
3. `remote.require(module)`
4. `remote.getCurrentWindow()` -获取主进程的窗体
5. `remote.getCurrentWebContents()` - 获取主进程的 webContent 属性
6. `remote.getGlobal(name)` -用于数据共享。
~~~
const { dialog } = require('electron').remote
dialog.showMessageBox({type: 'info', message: '在渲染进程中直接使用主进程的模块'})
~~~
## desktopCapturer
从桌面捕获音频和视频的媒体源的信息
## webFrame
自定义当前网页的呈现。
## contextBridge
![核心模块](https://img.kancloud.cn/ee/e8/eee8538d2aebd7f2901d450c41c77521_1331x784.png)
# 进程间通信
![](images/screenshot_1594105357343.png)
* 自主到从:从 Main 到 Renderer 的消息传递,借助`BrowerWindow.webContents.send()` 向所有渲染进程发送消息。
* 自从到主:从 Renderer 到 Main 的消息传递,借助`ipcRender`和`ipcMain`发送/接收消息。
* 事件机制:无论是`BrowerWindow.webContents.send()`,还是`ipc`,其实都是`node`的事件机制,都是`EventEmitter`的实例。
****
:-: 从渲染进程到主进程
* Callback 写法:
* ipcRenderer.send(channel, ...args)
* ipcMain.on(channel, handler)
* Promise 写法(Electron 7.0 之后,处理请求 + 响应模式):
* ipcRenderer.invoke(channel, ...args)
* ipcMain.handle(channel, handler)
> [主进程与渲染进程/主进程与webview通信](https://dushusir.com/electron-ipcmain-ipcrenderer/)
# 通知渲染进程
~~~
//主进程
const {ipcMain} = require('electron');
/**
* 接受渲染进程发送过来的数据
* 使用 event.sender.send() 于渲染进程通信
*/
ipcMain.on('msg', (event, data) => {
event.sender.send('reply','hi lee my name is yuan, age is 17'); // # 主进程接收到异步消息以后通知渲染进程
console.log(data);
})
~~~
## 主进程与渲染进程共享数据
[https://www.yuque.com/xiaoershangjiu-mattw/bhgamp/eyzdrz#hw8Jk](https://www.yuque.com/xiaoershangjiu-mattw/bhgamp/eyzdrz#hw8Jk)
## 渲染进程于渲染进程通信
1. 获取当前窗口的 id:
~~~
const winId = BrowserWindow.getFocusedWindow().id;
~~~
2. 通过 id 查找窗口:
~~~
let win = BrowserWindow.fromId(winId);
~~~
> [渲染进程于渲染进程通信](https://www.kancloud.cn/lihaotian001/electron-01/731073)
# preload
`electron`渲染进程默认为`nodejs`环境,在里面你可以调用`require`引入第三方模块,但有时候我们想要的是一个真实的浏览器环境。`electron`需要做的配置如下:
```
const mainWindow = new BrowserWindow({
resizable: true,
width: size.width,
height: size.height,
title: 'HolaStudio',
webPreferences:
{
nodeIntegration: false,
preload: path.join(__dirname, 'tangide', 'expose-window-apis.js')
}
});
```
创建`BrowserWindow`的时候指定`nodeIntegration`为`false`。 这样在`electron`内置浏览器里面不会有`module`和`require`全局变量。但 preload 文件 `expose-window-apis.js`仍可以访问;
preload 脚本文件,会在页面加载资源前就加载执行,保证了页面无论是在什么地方、什么时候调用注入接口都能调用到。
## 示例 preload 文件
`expose-window-apis.js` 文件的内容:
```
const { BrowserWindow } = require('electron')
const path = require('path')
const renderProcessApi = path.join(__dirname, './inject.js')
let win = new BrowserWindow({
webPreferences: {
preload: renderProcessApi
}
})
```
`preload.js`是一个特殊且单独的文件,在此文件中可以访问`node模块`、`electron`、`其他node 模块`提供的api,并挂载到`window`对象中,你其他的普通j avascript 代码就可以访问这些 api。
> [在Electron中最快速预加载脚本](https://www.cnblogs.com/lovesong/p/11161293.html)
> [Uncaught ReferenceError: require is not defined](https://github.com/electron/electron/issues/9920)
# asar
[asar 加密打包](https://electronjs.org/docs/tutorial/application-packaging),仅仅是对`resource/app`目录的封装,让人们不是那么容易看到你写的代码结果而已。
安装asar
```
npm install -g asar
asar p/pack your-app app.asar // 用 asar pack 打包
asar list xxxxxx.asar // 读取 asar 里面的文件
```
从 asar 包读取一个文件:
```
const fs = require('fs');
fs.readFileSync('/path/to/example.asar/file.txt');
```
列出 asar 包中根目录下的所有文件:
```
const fs = require('fs');
fs.readdirSync('/path/to/example.asar');
```
使用 asar 包中的一个模块:
```
require('/path/to/example.asar/dir/module.js');
```
使用 BrowserWindow 来显示一个 asar 包里的 web 页面:
```
const BrowserWindow = require('electron').BrowserWindow;
var win = new BrowserWindow({width: 800, height: 600});
win.loadURL('file:///path/to/example.asar/static/index.html');
```
# 参考
[Electron webview完全指南](http://www.ayqy.net/blog/electron-webview%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8D%97/)