Skip to content

electron 加载谷歌应用应用商店的插件方法

electron 加载 chrome line 插件

成功的效果演示

alt text

alt text

代码地址

提示

代码是付费的,提供售后服务,保证集成到你的 electron 项目中

如需要源码或者远程支持,请点击联系我们扫码添加微信

提示

例子使用 electron-vite 编写,"electron": "^31.0.2",支持 Windows10/Windows11/Mac/Linux(Windows7 需要修改 electron 为 22 版本)

Electron 中加载插件的三种方式

在 Electron 中,您可以通过三种主要方式加载插件,分别是:

方式适用场景优点缺点
BrowserWindow1.创建独立的应用窗口
2.需要加载插件或外部网页的独立窗口
1.完整的独立窗口,支持最大化、最小化、关闭等操作
2.独立渲染进程,资源隔离好
1.资源开销较大
2.不适合在一个窗口中嵌入多个页面
BrowserView1.在主窗口内嵌入多个独立操作的网页内容
2.更灵活的窗口布局控制
1.资源开销小,共享同一渲染进程
2.布局灵活,可嵌入多个视图
1.不具备完整的窗口操作功能
2.布局需要手动管理
3.其他的窗口 DOM 不能覆盖
WebView1.嵌入外部网页并与内容进行深度交互
2.在同一窗口中显示多个独立 Web 内容
1.提供高级交互能力
2.支持沙盒模式,提高安全性
3.像 iframe 一样引入内容
1.性能较差,尤其是在大量嵌入时
2.需额外管理安全风险

electron-vite WebView 的方式引入 line 插件

1. 引入@electron/remote 模块

具体添加代码如下:

electron-vite-添加-electron-remote-模块

2. 主进程定义加载插件

src/main/index.js
js
import { app, shell, BrowserWindow, ipcMain, session } from "electron";
import { join } from "path";
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import icon from "../../resources/icon.png?asset";
import "./titlebar";

require("@electron/remote/main").initialize();

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 900,
    height: 670,
    show: false,
    frame: false,
    autoHideMenuBar: true,
    ...(process.platform === "linux" ? { icon } : {}),
    webPreferences: {
      preload: join(__dirname, "../preload/index.js"),
      sandbox: false,
      webviewTag: true,
      webSecurity: false,

      nodeIntegration: true,
      contextIsolation: false,
    },
  });

  require("@electron/remote/main").enable(mainWindow.webContents);

  mainWindow.on("ready-to-show", () => {
    mainWindow.show();
  });

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url);
    return { action: "deny" };
  });

  // HMR for renderer base on electron-vite cli.
  // Load the remote URL for development or the local html file for production.
  if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
    console.log(
      "process.env['ELECTRON_RENDERER_URL']",
      process.env["ELECTRON_RENDERER_URL"] + "/index.html"
    );
    mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
  } else {
    mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
  }

  if (is.dev) {
    mainWindow.webContents.openDevTools();
  }
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  ipcMain.handle("isDev", () => {
    return is.dev;
  });

  ipcMain.handle("get-preload-js-path", async () => {
    let extensionPath;
    if (is.dev) {
      extensionPath = join(__dirname, "../preload/fillChromeAPI.js");
    } else {
      extensionPath = join(
        process.resourcesPath,
        "app.asar/out/preload/fillChromeAPI.js"
      );
    }
    return extensionPath;
  });

  ipcMain.handle("load-plugin", async () => {
    let extensionPath;
    if (is.dev) {
      extensionPath = join(
        __dirname,
        "../../line/ophjlpahpchlmihnnnihgmmeilfjmjjc/3.3.0_0"
      );
    } else {
      extensionPath = join(
        process.resourcesPath,
        "./line/ophjlpahpchlmihnnnihgmmeilfjmjjc/3.3.0_0"
      );
    }

    // persist:electron:line:extension 字符串中的内容不同,就会创建或获取不同的会话(session)分区

    const current_session = session.fromPartition(
      `persist:electron:line:extension`
    );

    // 清除所有存储数据
    await current_session.clearStorageData().then(() => {
      console.log("Storage data cleared");
    });

    console.log("extensionPath", extensionPath);
    return current_session.loadExtension(extensionPath);
  });

  createWindow();
});

这个 session 会自动删除吗?还是需要手动处理

在 Electron 中,通过 session.fromPartition('persist:...') 创建的持久化会话(即带有 persist: 前缀的分区)是不会自动删除的。它们的生命周期与应用程序相同,数据会一直保存在分区中,即使你关闭并重新打开应用程序,该分区的数据仍然会被保留。

如果你需要删除这个持久化会话的缓存、cookie 或其他存储数据,你需要手动处理。你可以使用 session 对象的相关方法来清除数据,例如:

javascript
const current_session = session.fromPartition(
  "persist:electron:line:extension"
);

// 清除缓存
current_session.clearCache().then(() => {
  console.log("Cache cleared");
});

// 清除所有cookies
current_session.clearStorageData({ storages: ["cookies"] }).then(() => {
  console.log("Cookies cleared");
});

// 清除所有存储数据
current_session.clearStorageData().then(() => {
  console.log("Storage data cleared");
});

如果你希望完全删除这个分区的数据,包括所有的存储内容,你需要显式调用这些清除方法。否则,该分区的数据将一直保留,直到你手动清除或者用户手动删除应用数据。

persist:electron:line:extension 这个字符不一样就是不一样的 session 吗?

是的,persist:electron:line:extension 字符串中的内容不同,就会创建或获取不同的会话(session)分区。

在 Electron 中,session.fromPartition('partition_name') 方法的 partition_name 参数用于区分不同的会话分区:

  • 如果 partition_namepersist: 为前缀,它将创建或获取一个持久化的会话分区。不同的 partition_name(即使只有一点点不同)会创建或获取完全独立的会话分区。
  • 如果 partition_name 没有 persist: 前缀,它将创建或获取一个非持久化的会话分区,这样的会话分区在应用程序关闭后会被自动删除。

例如:

  • session.fromPartition('persist:electron:line:extension')session.fromPartition('persist:electron:line:extension-v2') 将会创建或获取两个不同的持久化会话分区。
  • 每个会话分区都独立保存其缓存、cookie 和其他存储数据,互不干扰。

因此,如果你希望在应用中使用不同的会话上下文,只需要改变 partition_name 即可创建或获取不同的 session 对象。

3. 渲染进程加载 webview 插件

src/renderer/index/App.vue
vue
<template>
  <div>
    <Titlebar />

    <div id="mainbox" style="width: 100vw; height: calc(100vh - 60px)"></div>
  </div>
</template>

<script setup>
import { onMounted } from "vue";
import Titlebar from "./components/Titlebar.vue";

onMounted(async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  await window.api.loadPlugin();

  // 创建webview
  const webview = document.createElement("webview");
  webview.id = "myWebview";
  webview.allowpopups = "true";
  webview.src =
    "chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc/index.html";

  const preloadPath = await window.api.getPreloadJsPath();
  const isDev = await window.api.isDev();

  console.log("[preloadPath]", preloadPath);
  // 注入 preload.js
  webview.setAttribute(
    "preload",
    // `file://C:/Users/Administrator/Desktop/electron-line-plugin-app/src/preload/fillChromeAPI.js`,
    `file://${preloadPath}`
  );

  webview.setAttribute("partition", `persist:electron:line:extension`);
  webview.setAttribute("nodeintegration", true);
  webview.setAttribute("nodeintegrationinsubframes", true);
  webview.setAttribute(
    "webpreferences",
    "allowRunningInsecureContent,contextIsolation=false"
  );
  webview.style.width = "100%";
  webview.style.height = "100%";
  document.getElementById("mainbox").append(webview);
  console.log(webview.partition);

  // 等待 dom-ready 事件触发后调用 loadURL
  webview.addEventListener("dom-ready", () => {
    console.log("Webview is ready");
    console.log(webview.partition);
    // 打开开发者工具
    isDev && webview.openDevTools();
  });
});
</script>

<style scoped></style>

提示

上面代码可以优化 使用 will-attach-webview 进行加载 preload.js

js
const preloadPath = await window.api.getPreloadJsPath();
const isDev = await window.api.isDev();

console.log("[preloadPath]", preloadPath);
// 注入 preload.js
webview.setAttribute(
  "preload",
  // `file://C:/Users/Administrator/Desktop/electron-line-plugin-app/src/preload/fillChromeAPI.js`,
  `file://${preloadPath}`
);

https://cn.electron-vite.org/guide/dev#webview

将预加载脚本附加到 webview 的最简单方法是通过 webContents 的 will-attach-webview 事件处理。

js
mainWindow.webContents.on("will-attach-webview", (e, webPreferences) => {
  webPreferences.preload = join(__dirname, "../preload/index.js");
});

获取 chrome 插件源码

1. 引入Chrome extension source viewer插件详情页面

https://chromewebstore.google.com/detail/chrome-extension-source-v/jifpbeccnghkjeaalbbjmodiffmgedin

2. 添加至Chrome扩展

alt text

alt text

3. 打开你的插件列表,然后打开 CRX 的详情

浏览器地址栏输入:

chrome://extensions/

alt text

4. 把 Chrome extension source viewer 插件固定到工具栏

alt text

5. 添加 line 插件

https://chrome.google.com/webstore/detail/ophjlpahpchlmihnnnihgmmeilfjmjjc

alt text

6. 打开 line 插件的详情页面,并且固定到工具栏

chrome://extensions/?id=ophjlpahpchlmihnnnihgmmeilfjmjjc

alt text

alt text

7. 下载 line 插件源码

alt text

alt text