一、Webview

Electron 中 webview 是 electron 框架自带的组件,用于加载 web 应用。与 web 通信的原理如下:

  • webview => web

    1. // webview 发送
    2. webview.send('channel_name_in_react_component', args);
    3. // web 监听
    4. ipcRenderer.on('channelNamel', (event, args) {
    5. // do whatever
    6. });
  • web => webview ``` // web 发送 import {ipcRenderer} from ‘electron’ ipcRenderer.sendToHost(‘channelName’, …args)

// webview 监听 webview.addEventListener(‘ipc-message’, onIpcMessage)

function onIpcMessage(event) { const {args, channel} = event console.log(channel, args) }

  1. 这些逻辑一般封装在一个 bridge.js 里,然后让 webview 预加载这个文件
  2. ```javascript
  3. // bridge.js
  4. const { ipcRenderer } = require("electron");
  5. const SupportedMethods = ["send"];
  6. class WebViewBridge {
  7. constructor() {
  8. window.WebView = window.CommonWebView = this.getWebView();
  9. this.listen();
  10. }
  11. getWebView() {
  12. const WebView = {};
  13. SupportedMethods.forEach((api) => {
  14. WebView[api] = (params) => {
  15. this._webviewMessageSender(api, params);
  16. };
  17. });
  18. return WebView;
  19. }
  20. _webviewMessageSender(method, params) {
  21. try {
  22. params = JSON.parse(params);
  23. } catch (e) {
  24. console.error(`
  25. [WebViewBridge] PARSE BASE64 ERROR
  26. params: ${params}
  27. e: ${e}
  28. `);
  29. return;
  30. }
  31. ipcRenderer.sendToHost(method, params.arguments[0]);
  32. }
  33. listen() {
  34. ipcRenderer.on("electron-webview-channel", (event, callbackName, data) => {
  35. try {
  36. console.log("==", event, callbackName, data);
  37. window[callbackName](data);
  38. } catch (e) {
  39. console.error(`
  40. [WebViewBridge] CALLBACK ERROR
  41. callbackName: ${callbackName}
  42. data: ${data}
  43. detail: ${e}
  44. `);
  45. }
  46. });
  47. }
  48. }
  49. new WebViewBridge();
  • 使用 webview 时要注意开启两点: ```javascript const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, webviewTag: true, // 1、启用 webview }, });

<!DOCTYPE html>

  1. <a name="hWLIC"></a>
  2. ## 二、BrowserWindow
  3. 主进程使用<br />1、用于在主进程创建和控制浏览器窗口,可以创建出无边框窗口(可以自定义header)、透明窗口(用于制作不规则界面)、父子窗口、模态窗口<br />2、往往需要通过 -webkit-app-region 设置可拖拽区域:
  4. ```html
  5. <body style="-webkit-app-region: drag"></body>
  6. button {
  7. -webkit-app-region: no-drag;
  8. }

3、为避免闪烁,展示窗口的时机应该在 ready-to-show 或者 did-finish-load 事件之后

  1. const { BrowserWindow } = require('electron')
  2. const win = new BrowserWindow({ show: false })
  3. win.once('ready-to-show', () => {
  4. win.show() // 如果还是有白屏现象,可以换成 did-finish-load 事件
  5. })

4、常用事件

  • close、closed 将要关闭和已经关闭
  • blur、focus、show、hide、maxmize、unmaxmize、minimize、restore

5、常用静态方法

  • getAllWindows
  • getFocusedWindow
  • fromWebContents
  • fromBrowserView
  • fromId

6、常用属性和方法

  • id
  • webContents
  • close() 和用户点击关闭按钮效果一样,关闭操作可能被 web 取消
  • destroy() 强制关闭窗口,只会触发 closed;不会触发 close、beforeunload、unload
  • focus()、blur()、isFocused()、isDestroyed()、isVisible()、isModal()、minimize()、restore()
  • show()、hide()
  • loadUrl(url,options) url 可以是远程地址 (例如 http://),也可以是 file:// 协议的本地HTML文件的路径. 与 webContents.loadUrl(url,options) 功能相似: ```javascript // 确保 url 符合要求 const url = require(‘url’).format({ protocol: ‘file’, slashes: true, pathname: require(‘path’).join(__dirname, ‘index.html’) })

win.loadURL(url)

  1. - loadFile(file) webContents.loadFile(file) 功能相似:
  2. <a name="QJcuk"></a>
  3. ## 三、webContents
  4. 主进程使用<br />1webContents EventEmitter. 负责渲染和控制网页, BrowserWindow 对象的一个属性。
  5. ```javascript
  6. const { BrowserWindow } = require('electron')
  7. const win = new BrowserWindow({ width: 800, height: 1500 })
  8. win.loadURL('http://github.com')
  9. const contents = win.webContents
  10. console.log(contents)

2、可以通过 electron 直接引入

  1. const { webContents } = require('electron')
  2. console.log(webContents)

3、常用静态方法

  • getAllWebContents(),包含所有Windows,webviews,opened devtools 和 devtools 扩展背景页的 web 内容
  • getFocusedWebContents()
  • fromId()

4、常用事件

  • dom-ready
  • did-finish-load
  • render-process-gone
  • plugin-crashed
  • destroyed
  • ipc-message IPC 通信

5、常用实例方法和属性

  • downloadURL() 下载文件,开始后会触发对应 session 的 will-download 事件 ```javascript // 触发下载 win.webContents.downloadURL(url)

// 监听 will-download session.defaultSession.on(‘will-download’, (event, item, webContents) => { // 监听 item 的 pause、resume、cancel、updated、done 等事件处理下载逻辑 })

  1. - goBack()
  2. - goForward()
  3. - canGoBack()
  4. - canGoForward()
  5. - setUserAgent()
  6. - postMessage() 主进程发送事件,渲染进程接受 ipcRender.on
  7. - id
  8. - session
  9. - userAgent
  10. <a name="SsBDI"></a>
  11. ## 四、session
  12. 主进程使用<br />管理浏览器会话、cookie、缓存、代理设置等。
  13. ```javascript
  14. const { BrowserWindow } = require('electron')
  15. const win = new BrowserWindow({ width: 800, height: 600 })
  16. win.loadURL('http://github.com')
  17. const ses = win.webContents.session
  18. console.log(ses.getUserAgent())

1、静态方法和属性

  • fromPartition(partition),如果 partition 以 persist:开头, 该页面将使用持续的 session,并在所有页面生效,且使用同一个partition. 如果没有 persist: 前缀, 页面将使用 in-memory session. 如果没有设置partition,app 将返回默认的session。
  • defaultSession 应用程序的默认session对象

2、实例属性和方法

  • clearStorageData(options),可以清除:appcache, cookies, filesystem, indexdb, localstorage, shadercache, websql, serviceworkers, cachestorage
  • setUserAgent()
  • loadExtention() 设置扩展程序 ```javascript const { app, session } = require(‘electron’) const path = require(‘path’)

app.on(‘ready’, async () => { await session.loadExtension( path.join(__dirname, ‘react-devtools’), // allowFileAccess is required to load the devtools extension on file:// URLs. { allowFileAccess: true } ) // Note that in order to use the React DevTools extension, you’ll need to // download and unzip a copy of the extension. })

  1. - cookies
  2. ```javascript
  3. import { Cookie, CookiesGetFilter, CookiesSetDetails, session } from 'electron';
  4. import { fileURLToPath } from 'url';
  5. const SessionPartition = 'persist:my-app';
  6. /**
  7. * PersistentSession
  8. * Session 持久化管理
  9. */
  10. class PersistentSession {
  11. private _session: session | null = null;
  12. init() {
  13. if (this._session) {
  14. return;
  15. }
  16. this._session = session.fromPartition(SessionPartition, { cache: false });
  17. // workaround in https://github.com/electron/electron/issues/23757
  18. this._session.protocol.registerFileProtocol('file', (request, callback) => {
  19. callback({ path: fileURLToPath(request.url) });
  20. });
  21. }
  22. get session(): session {
  23. if (!this._session) {
  24. this.init();
  25. }
  26. return this._session;
  27. }
  28. get partition(): string {
  29. if (!this._session) {
  30. this.init();
  31. }
  32. return SessionPartition;
  33. }
  34. async getCookies(filter: CookiesGetFilter): Promise<Cookie[]> {
  35. try {
  36. return await this.session.cookies.get(filter);
  37. } catch (error) {
  38. return [];
  39. }
  40. }
  41. async setCookies(details: CookiesSetDetails): Promise<void> {
  42. try {
  43. await this.session.cookies.set(details);
  44. } catch (error) {}
  45. }
  46. async removeCookies(url: string, name: string): Promise<void> {
  47. try {
  48. await this.session.cookies.remove(url, name);
  49. } catch (error) {}
  50. }
  51. async clearAllCookies(): Promise<void> {
  52. try {
  53. await this.session.clearStorageData({ storages: ['cookies'] });
  54. } catch (error) {}
  55. }
  56. async flushCookies(): Promise<void> {
  57. try {
  58. await this.session.cookies.flushStore();
  59. } catch (error) {}
  60. }
  61. }
  62. export const persistentSession = new PersistentSession();