最近想給上次鼓搗出的 搜索 App 加個搜索當前頁面文字的功能淋硝。
開工前举农,覺得這應該是個有現成解的問題侦啸,很快找到了一個 Electron 組件 electron-in-page-search梅鹦⊥患眨看了一遍項目的 READEME
鲁豪,覺得是個認真的項目潘悼,文檔也算豐富,提供了示例代碼爬橡,心想應該不用花太多時間便能收工了治唤。
但開始動手后,便很快發(fā)現了問題堤尾。因為對 Electron 不夠熟悉肝劲,不知道示例代碼該“貼”到哪里!
急切地看了下 Electron 的入門文檔郭宝,了解到可以利用 BrowserWindow
的 preload 屬性娜饵,注入一個腳本文件。又看了下之前由 Nativefier 生成的代碼频鉴,它已經寫好了一個 preload.js 實現桃犬,其中預留了一個擴展點,../../inject/inject.js
衔统,如果這個文件存在鹿榜,preload.js 就會加載它。
var needToInject = _fs2.default.existsSync(INJECT_JS_PATH);
if (needToInject)
require(INJECT_JS_PATH);
Nativefier 真是貼心锦爵,不要辜負它舱殿,在 ../../inject/
目錄下創(chuàng)建出 inject.js
,填入 electron-in-page-search
的示例代碼
const searchInPage = require("electron-in-page-search").default;
const remote = require("electron").remote;
const searchInWindow = searchInPage(remote.getCurrentWebContents());
searchInWindow.openSearchWindow();
重新打開 App险掀,可以看到頁面右上角沪袭,出現了預期的搜素框。
接下來樟氢,為了更符合平時的軟件使用習慣冈绊,想在 App 的菜單欄中,添加一個菜單項埠啃,Find In Page
死宣,并綁定一個快捷鍵,?F
碴开。頁面顯示時毅该,不顯示搜索框,菜單項被觸發(fā)時才顯示出來叹螟。
在 main.js
中找到定義菜單的地方鹃骂,仿照其他已存在的菜單項,添加新菜單項的配置
function createMenu(_ref) {
...
var template = [{...
},{
label: 'Find In Page',
accelerator: 'CmdOrCtrl+F',
click: function click() {
// showSearchInWindow();
}
}, ...];
var menu = _electron.Menu.buildFromTemplate(template);
_electron.Menu.setApplicationMenu(menu);
}
運行程序罢绽,看下效果
這里預留了一個未實現的 showSearchInWindow
方法 畏线,作用是顯示搜索框。那么問題來了良价,在 main.js
中如何執(zhí)行 searchInWindow.openSearchWindow()
這行代碼呢寝殴?
這里要補充下前文跳過的 Electron 中主進程
和渲染進程
兩個概念蒿叠。
main.js
運行在主進程
中,而 preload
引入的腳本文件蚣常,運行在渲染進程
中市咽。
另外一方面,一個瀏覽器實例抵蚊,可以有多個瀏覽窗口施绎,但只會有一個菜單欄
, 共享給所有瀏覽窗口。菜單欄定義在主進程
贞绳,每個瀏覽窗口都是一個獨立的渲染進程
谷醉,而像搜索框
這樣的與瀏覽窗口相關的對象,也需要是產生和運行在對應的渲染進程
上冈闭,這也是前面將示例代碼放在 inject.js
中的原因俱尼。
搞明白這些,就能想到萎攒,并不能將 searchInWindow
對象定義在主進程上遇八,來讓菜單被點擊時,調用該對象的方法耍休∪杏溃可行的方式是,當菜單被點擊時羊精,讓主進程通知渲染進程揽碘,間接地達到目的。
于是我們需要了解园匹,在 Electron 中,主進程如何和渲染進程通信
In Electron, we have several ways to communicate between the main process and renderer processes. Like ipcRenderer
and ipcMain
modules for sending messages, and the remote module for RPC style communication.
這里提到了兩種方式劫灶,第一種方式提到了 ipcMain
和 ipcRenderer
裸违。IPC
,有些讀者看到這個詞應該不陌生本昏,Inter-Process Communication
供汛,進程間通信,看名字就覺得涌穆,這種方式就夠用了怔昨。再參考下鏈接中的示例代碼,編寫如下實現
在 main.js
中
var showSearchInWindow = function () {
mainWindow.webContents.send('find-in-page');
};
在 inject.js
中
ipcRenderer.on('find-in-page', (event, arg) => {
console.log(' ipcRenderer find-in-page');
search.openSearchWindow();
});
可以看到宿稀,主進程發(fā)出了 find-in-page
這個事件趁舀,在渲染進程中我們監(jiān)聽著這個事件,當事件發(fā)生時祝沸,執(zhí)行相應的操作矮烹。
回顧一下越庇,主進程和渲染進程,是 Electron 中最基礎的兩個概念奉狈,并不復雜卤唉,但要明確區(qū)分。preload
是 Electron 為瀏覽窗口預置的一個擴展點仁期,執(zhí)行時機是在頁面的其他腳本執(zhí)行之前桑驱。
此次分享,實際上省略了一些爬坑的過程跛蛋,比如調試
熬的,比如 nodeIntegration
,后面希望有機會再作些探索问芬,寫一些這些方面的內容悦析。