pc網頁端調起客戶端應用的那些事--electron

Date: 2020.04.11

前言

 在當下大前端的趨勢之下,我還是跟隨大流跳入了electron 開發的坑。不過這個也讓我不再侷限於網頁業務的開發,並對大前端所代表的的內容有了新的認識。如果是在去年聽到大前端這個詞,我也只是只知其名不懂其義。

 爲了開發客戶端的新業務,這三個月我接觸了很多新知識,從基本的業務開發,electron 的構建配置和打包,針對 mac 應用的簽名,公證和蓋章, pc 客戶端的ota升級,再到 jenkins 自動化構建配置,CI集成,再到現在的pc網頁端調起客戶端應用等業務。這些內容提高了還是前端切圖仔的我的眼界【捂臉.jpeg】

 不過,這個三個月一直忙於業務開發,學到了很多內容,一直沒有進行過總結,這個讓我覺得很害怕,害怕學到的新知識會被忘掉,因此,今天還是趕緊針對這些知識點做一下部分總結。今天先整理一下pc網頁端調起客戶端應用的知識點。

正文

背景:今天要討論的是在網頁端打開客戶端頁面的功能,也許大家比較少接觸過在 pc 端調起 pc 客戶端的應用的功能;但是在移動端網頁調起手機應用的操作,大家或多或少有接觸過;其實無論在 pc 還是網頁;它們實現調起客戶端的原理都是類似的。接下來我會分享一下 pc 網端調起 eletron 客戶端的那些事。

part 1: 如何在瀏覽器調起基於electron 開發的應用(原理)

  1. 基於自定義的僞協議(URL Scheme 協議),實現應用的調起
  2. URL Scheme 協議【理解爲用於實現在瀏覽器跳轉應用的協議就可以啦】

mac

配置 URL Schemes,往info.plist文件裏配置好scheme。

Mac os上瀏覽器啓動本地app主要是通過註冊 URL Schemes,每個打包好的app文件中都會存在一個info.plist文件;

mac 用戶可以嘗試在配置了URL Schemes的應用中,查看已經安裝好的應用的 URL Schemes 配置,步驟如下:

  1. 在應用程序中,找到對應的應用
  2. 右鍵顯示包內容
  3. 打開 contents 文件
  4. 點擊查看 Info.plist 找到 URL types – URL Schemes;
  5. 再通過瀏覽器打開輸入該協議即可實現在瀏覽器調起應用。

以 xcode 爲例:
6. 打開 xcode 的 Info.plist 文件。可看到其中一個 URL Schemes 爲 xcode.
在這裏插入圖片描述
7. 再打開瀏覽器,在地址欄輸入xcode://,即可調起 xcode 應用。
在這裏插入圖片描述
在這裏插入圖片描述

window

window 的調起類似;

只不過不是往info.plist文件裏配置scheme,而是通過往註冊表的 HKCR (HKEY_CALSSES_ROOT) 目錄下添加一條記錄來完成該協議的註冊。

示例:在註冊表的HKEY_CALSSES_ROOT目錄下,添加一個xcode 字段,並將值指向 xcode 的 exe 包的路徑;即可實現如上面 mac 的方式在瀏覽器調起應用。

暫時沒有window 電腦在身邊,使用了網上一張圖作爲例子,侵刪
在這裏插入圖片描述

part 2: 那麼如何在開發應用的時候定義好協議呢(怎麼做)

其實關於 electron 應用協議的註冊,electron 已經有提供相應的 api 啦 (app.setAsDefaultProtocolClient),我們可以直接使用。在進行 app 的相關事件監聽和處理時,同時進行協議的註冊。

// 示例1:比如說想通過在瀏覽器輸入 “協議://”,就可以實現調起該應用,則實現如下
app.setAsDefaultProtocolClient("你的協議");

// 示例2:比如說想通過在瀏覽器輸入 “xcode://”,按照下面進行協議的註冊,就可以實現調起該應用
app.setAsDefaultProtocolClient("xcode");

另外,還需要在 package.json 的 build 字段中的 protocols 對象進行 schemes 配置。

在mac中,該配置字段主要是用在將 schemes的值打包進 Info.plist 文件的URL Schemes。【如果沒有進行這個配置,在第一次安裝應用但未啓動,直接通過網頁端調起該應用,好像是無效的,除非已手動打開過一次該應用,主要是因爲app.setAsDefaultProtocolClient(“你的協議”)這個操作是寫在主進程裏面,需要執行一遍應用的邏輯,即啓動過應用,才能完成該協議的註冊】。

window 只需要使用 app.setAsDefaultProtocolClient 在註冊表進行註冊即可。

  • 查看協議是否設置成功
  1. 分別進行mac 和window的打包
  2. mac 查看包的 Info.plist (URL types – URL Schemes)
  3. window 查看註冊表
  • 調用方式

可以嘗試在瀏覽器直接打開 註冊的協議://,就可以完成瀏覽器的調起

part 3: 調起應用時觸發的鉤子函數

  1. mac 通過 app.on(‘open-url’, fun)監聽
  2. window 通過 app.on(“second-instance”,fun)監聽

mac 和 window有區別,開發和生產有區別

macOS 下通過URL Schemes協議調起應用時

主實例會通過 open-url 事件監聽應用被調起的事件

// 網頁進行應用的調起後,會觸發該事件
app.on('open-url', (event, urlStr) => {
  console.log("mac 準備執行網頁端調起客戶端邏輯");
  handleUrlFromWeb(urlStr); // 對 url 執行的處理邏輯
  _handleBeforeOpenWindowByWeb(); // 利用相應的參數,打開其他頁面(這個是我這邊的需求用到),代碼略
});

  • macOS中,打開協議的參數的處理
// 進行處理網頁傳來 url 參數,參數自定義,以下爲示例
// 示例調起應用的 url 爲(應用URL Schemes爲youapp):youapp:?name=名字&type=類型&shareId=1585876954860136091
const handleUrlFromWeb = (urlStr) => {
  const urlObj = new URL(urlStr);
  const { searchParams } = urlObj;
  const shareId = searchParams.get('shareId');
  const name = searchParams.get('name');
  const type = searchParams.get('type');
  /** code
    這部分代碼爲使用這些參數執行相應的邏輯
  */
};

window 下通過URL Schemes協議調起應用時

分兩種情況

  1. 應用處於打開狀態,會觸發 second-instance 事件並接收這個 URL。

  2. 應用處於未打開狀態,在網頁端通過瀏覽器調起應用之後不會觸發 second-instance 事件;這個時候需要主動判斷應用是否是從網頁端調起,並主動觸發 second-instance 事件;

在 window 裏面判斷是否是從網頁端的標準:如果是通過url schema啓動,其啓動參數會超過1個

以下爲在應用 ready 之後,判斷是否是在 window 下主動調起客戶端,並主動觸發 second-instance 事件的實現邏輯;

/**
 * 添加對協議鏈接的處理, 用於實現網頁調起客戶端邏輯
 */
// 當應用啓動完成後,主動判斷應用是否是從網頁中調起
const _handleAfterReady = () => {
  // windows如果是通過url schema啓動則發出時間處理
  // 啓動參數超過1個纔可能是通過url schema啓動
  if (process.argv.length > 1) {
    if (!app.isReady()) {
      app.once("browser-window-created", () => {
        // app 未打開時,通過 open-url打開 app,此時可能還沒 ready,需要延遲發送事件
        // 此段ready延遲無法觸發 service/app/ open-url 處理,因爲saga初始化需要時間
        app.emit("second-instance", null, process.argv);
      });
    } else {
      app.emit("second-instance", null, process.argv);
    }
  }
};

觸發 second-instance 事件之後的具體邏輯與 mac 類似,不過針對獲取到的參數的處理方式還是存在一些差異

app.on('second-instance', async (event, argv) => {
  // Windows 下通過協議URL啓動時,URL會作爲參數,所以需要在這個事件裏處理
  if (process.platform === 'win32') {
    console.log("window 準備執行網頁端調起客戶端邏輯");
    handleArgvFromWeb(argv);
    _handleOpenWindowByWeb();
  }
});

處理window下的參數

const PROTOCOL = '註冊的協議名'; // 用戶自定義

// window 系統中執行網頁調起應用時,處理協議傳入的參數
const handleArgvFromWeb = (argv) => {
  const prefix = `${PROTOCOL}:`;
  // 開發階段,跳過前兩個參數(`electron.exe .`)
  // 打包後,跳過第一個參數(`myapp.exe`)
  const offset = app.isPackaged ? 1 : 2;
  const url = argv.find((arg, i) => i >= offset && arg.startsWith(prefix));
  if (url) handleUrlFromWeb(url);
};

補充: 從網頁調起客戶端時,在macos 中,既會觸發 open-url 事件,也會觸發 second-instance 事件;所以在寫 second-instance事件 處理邏輯時,需要將 mac 排除掉,避免重複處理(通過判斷 process.platform === ‘win32’【我們的應用只有Mac和window】)

part 4: 將被調起應用的可能存在的狀態

這個部分主要是針對本人當下所做的需求去進行分析: 調起應用,根據是否登錄,再決定是否打開新窗口並執行相應的業務邏輯(可以打開多個窗口)。按需瀏覽,業務不同可以考慮跳過該part,直接看下一部分。

  1. 已打開,已登錄

這個時候,直接在瀏覽器通過協議打開即可

  1. 已打開,未登錄

瀏覽器通過協議打開,顯示登錄窗口,登錄成功,顯示主窗口個,並打開對應功能窗口(要注意多窗口的處理,用戶有可能在多個網頁中調起了應用)

  1. 未打開,已登錄 和 未打開,未登錄
    如果只是使用 app.setAsDefaultProtocolClient(“你的協議”)進行 scheme 協議的註冊;那麼剛安裝完應用,首次需要用戶主動打開應用,才能完成 scheme 協議的註冊;

如果想要在應用剛安裝完成,不要求用戶首次主動打開應用,也支持scheme 協議(通過網頁調起)調起應用,則需要做一下配置

  • mac 應用,在package.json 的 build 字段中的 protocols 對象進行 schemes 配置即可。打包完成後,會在應用的 info.plist 看到相應的協議配置

  • window 應用,在需要通過在 package.json 中的 nsis 字段中配置相應的安裝或卸載相關的腳本路徑;配置應用在安裝時,主動往註冊表寫入協議;
    在這裏插入圖片描述

  • 瀏覽器判斷 scheme 協議是否存在(判斷本地是否有安裝應用,沒安裝跳下載地址)

part 5: 關於卸載的那些事

mac 卸載存在的坑:暫時沒有發現

macOS 相關的卸載會比較簡單,只在卸載應用即可完成卸載

window 卸載存在的坑:electron 開發的應用已完成卸載,但在註冊表通過app.setAsDefaultProtocolClient 註冊的協議沒有刪除

問題

但是針對 window 的應用的卸載後會有坑,直接通過 app.setAsDefaultProtocolClient(“你的協議”)可以完成協議的寫入,但是應用卻不會在卸載時主動刪除註冊表的協議;

這個時候就會存在一個坑,用戶在瀏覽器再輸入相應協議進行應用的調起時,瀏覽器依舊會彈出相應的提示彈窗(這個圖是在mac 截的,window類似),點擊打開應用,彈窗會被收起,但實際上並不會打開應用,因爲應用已經被卸載,只是註冊表的記錄沒被刪除,導致依舊會出現彈窗。
在這裏插入圖片描述
這種情況其實並不會影響正常功能的使用了,但是測試人員說這個不是很美觀。好吧,不美觀,就再處理一下。

解決

這種情況應該就應用卸載的邏輯做一下配置才行。還記得上一下小部分,有說過可以通過 nsis 去進行應用的安裝和卸載的相關配置(在我們應用中,window打包使用的是 electron-builder);

在 electron-builder 的說明文檔中 nsis 模塊的部分,有提供8個配置入口(customHeader, preInit, customInit, customUnInit, customInstall, customUnInstall, customRemoveFiles, customInstallMode),我將它們理解爲插槽,用於針對安裝和卸載去進行定製化配置

使用示例如下:

!macro customHeader
  !system "echo '' > ${BUILD_RESOURCES_DIR}/customHeader"
!macroend

!macro preInit
  ; This macro is inserted at the beginning of the NSIS .OnInit callback
  !system "echo '' > ${BUILD_RESOURCES_DIR}/preInit"
!macroend

!macro customInit
  !system "echo '' > ${BUILD_RESOURCES_DIR}/customInit"
!macroend

!macro customInstall
  !system "echo '' > ${BUILD_RESOURCES_DIR}/customInstall"
!macroend

!macro customInstallMode
  # set $isForceMachineInstall or $isForceCurrentInstall 
  # to enforce one or the other modes.
!macroend

我們的需求是在應用卸載時主動刪除註冊表的相關的協議,所以 installer.nsh 邏輯如下:

!macro customUnInstall
    DeleteRegKey HKCR "你註冊的協議"
!macroend 

以上即在應用卸載時完成協議的刪除

其他補充

  • HKCR 表示的 HKEY_CLASSES_ROOT 的在註冊表的簡寫 [即註冊表的路徑]

  • nsis鉤子詳細說明:https://www.electron.build/configuration/nsis
    在這裏插入圖片描述

  • 參考寫法(卸載程序):https://stackoom.com/question/3C5eL/nsis%E5%8D%B8%E8%BD%BD%E7%A8%8B%E5%BA%8F%E6%9C%AA%E5%88%A0%E9%99%A4%E7%94%B5%E5%AD%90%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%B3%A8%E5%86%8C%E8%A1%A8-nsh%E8%84%9A%E6%9C%AC

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章