想用Electron做個小工具?這個或許是終極版

muwooo發表於2021-07-12

故事背景

之前在網上有看到很多小夥伴基於 electron 實現了非常多好用的桌面端工具,比如圖床管理工具 PicGo,就專門做圖床工具。也有一些其他的類似的小工具,比如 saladict-desktop 專門做沙拉翻譯查詞的桌面端應用,colorpicker 專做桌面端取色工具...

我們也參考了這些小工具的設計理念,嘗試在公司內部做一款桌面端工具,解決網路抓包、代理、圖床、效能測評等常見場景的使用問題。最後在推廣的時候,遇到了一個比較嚴重的問題,就是很多小工具對特定使用者來說並不需要。比如測試只需要使用網路抓包、代理的功能,其他功能並不關心。此時就需要設計一款桌面端應用,類似於 App Store 那樣,用到什麼下載安裝什麼即可。這就需要實現桌面端應用的外掛化。

於是乎,我們看到了 uTools 是支援外掛化的桌面端應用,但是前提是我們的外掛必須釋出到 uTools 外掛市場,才能實現多端同步下載的功能,但是公司內部的工具庫有些涉及到安全資訊又無法釋出到 uTools 外掛中,所以我們特別渴望有一款類似於 uTools 的內部工具箱。

為了進一步提高開發工作效率,最近我們基於 electron 開發了一款媲美 uTools 的開源工具箱 rubick。該工具箱不僅僅開源,最重要的是可以使用 uTools 生態內所有開源外掛!這將是巨大的能力,意味著 uTools 生態內所有外掛可以無差異化使用到 rubick 中。

QQ20210705-210753.gif

程式碼倉庫:https://github.com/clouDr-f2e/rubick

外掛化之旅

一開始想到做外掛化,無非就是使用 electronwebview 能力,實現類似於原生內嵌h5那樣的方式,h5 頁面可以做獨立釋出,原生提供 nativaAPI 之間通過 jsBridge 來橋接呼叫原生的方法。這樣實現並無問題,我們也嘗試了做了一次。最終思路大概是:

electron webview 方式

1. electron 中使用 webview

<webview src="https://xxx.xx.com/index.html" preload="preload.js" />

2. 實現 bridge

// preload.js
window.rubickBridge = {
  sayHello() {
    console.log('hello world')
  }
}

3. 外掛藉助 bridge 呼叫 electron 的能力

<html>
 <body>
     <div>這是一個外掛<div>
 </body>
 <script>
  window.rubickBridge.sayHello()
</script>
</html>

4. 通訊

因為 proload.jselectronrenderer 程式的,所以如果需要使用部分 main 程式的能力,則需要使用通訊機制:

// main process
ipcMain.on('msg-trigger', async (event, arg) => {
    const window = arg.winId ? BrowserWindow.fromId(arg.winId) : mainWindow
    const operators = arg.type.split('.');
    let fn = Api;
    operators.forEach((op) => {
      fn = fn[op];
    });
    const data = await fn(arg, window);
    event.sender.send(`msg-back-${arg.type}`, data);
});
  
// renderer process
ipcRenderer.send('msg-trigger', {
  type: 'getPath',
  name,
});
ipcRenderer.on(`msg-back-getPath`, (e, result) => {
  console.log(result)
});

為什麼後來我們又放棄了這條路? ?

其實上面的思路大致是沒啥問題的,我們也基於上面的思路成功把功能抽成了外掛,按照外掛的方式進行安裝載入。直到我們注意到 utools 的強大,感覺 utools 的生態非常豐富,我們要是能整合 utools 的生成那該多好呀!所以我們秉持著幹不過他就成為他的原則,我們嘗試著成為他。但是 utools 本身並沒有開源,所以沒有辦法去吸取一些優秀的程式碼實現,但是我們可以看他的官方文件。

我們發現其實 utools 大多數外掛都是和 container 層分離的,也就是說 utools 只是一個外掛的容器,為外掛提供了一些 api 能力和方法。所以一旦我們實現了utools載入外掛的能力,實現 utools 的所有 API 函式,是不是就約等於實現了 utools ! 我們就可以使用 utools 的外掛?

utools 方式

按照 utools 的 文件,首先我們需要實現一個外掛,必須要有個 plugin.json,這玩意就是用來告訴 utools 外掛的資訊。我們也按照文件來寫:

{
    "pluginName": "helloWorld",
    "description": "我的第一個uTools外掛",
    "main": "index.html",
    "version": "0.0.1",
    "logo": "logo.png",
    "features": [
        {
          "code": "hello",
          "explain": "hello world",
          "cmds":["hello", "你好"]
        }
    ]
}

接下來是將寫好的外掛用 utools 跑起來,按照 utools的互動是複製 plugin.jsonutools搜尋框即可,我們也可以實現:

// 監聽 input change
// 讀取剪下板內容
const fileUrl = clipboard.read('public.file-url').replace('file://', '');
// 複製檔案
if (fileUrl && value === 'plugin.json') {
  // 讀取 plugin.json 配置
  const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8'));
  const pluginConfig = {
    ...config,
    // index.html 檔案位置,用於webview載入
    sourceFile: path.join(fileUrl, `../${config.main || 'index.html'}`),
    id: uuidv4(),
    type: 'dev',
    icon: 'image://' + path.join(fileUrl, `../${config.logo}`),
    subType: (() => {
      if (config.main) {
        return ''
      }
      return 'template';
    })()
  };
}

實現效果如下:

image.png

接下來就是進行命令搜尋外掛:

image.png

實現這個功能其實也就是對之前儲存的pluginConfig的裡面的 features 進行遍歷,找到相應的 cmd 後進行下拉框展示即可。

然後我們要去實現選擇功能,用 webview 載入頁面的能力:

<template>
  <div>
    <webview id="webview" :src="path" :preload="preload"/>
  </div>
</template>
<script>
export default {
  data() {
    return {
      path: `File://${this.$route.query.sourceFile}`,
      preload: `File://${path.join(__static, './preload.js')}`,
      webview: null,
      query: this.$route.query,
      config: {}
    }
  }
}
</script>

image.png

到此結束了?並沒有!!!由於篇幅的原因,我們後續再說。本出寫的外掛demo已上傳github: https://github.com/clouDr-f2e/rubick-plugin-demo

目前支援能力

載入utools生態外掛

github 上開源的 鬥圖 外掛舉例,要載入鬥圖外掛,只需要將程式碼 clone下來後,複製其 plugin.json 進入搜尋框即可使用

鬥圖:https://github.com/vst93/doutu-uToolsPlugin

image.png

超級皮膚

長按滑鼠右鍵,即可呼起超級皮膚,可以根據當前滑鼠選擇內容,匹配對應外掛能力。比如當前選擇圖片後長按右擊,則會呼起上傳圖床外掛:

image.png

模板

為了更貼合 uTools 的外掛能力,需要實現模板功能,模板即是一個內建 UI 樣式的功能外掛。

image.png

utools 自帶的系統命令

取色

image.png

截圖

image.png

全域性快捷鍵

image.png

最後

目前 rubick 已經實現 utools 大多數核心能力,最重要的是可以使用 utools 所有生態 ! 更多能力可以前往 github 體驗。如果感覺有用,可以幫忙反手一個 star ✨

Rubick github

相關文章