桌面端框架Electron使用問題整理和總結

在這裏插入圖片描述
文章主要記錄了使用electron日常開發中所遇到的一些坑以及怎樣填坑,幫助其他開發的小夥伴少踩一些坑。建議關注收藏,以便遇到時候方便查閱!

electron這個框架我就不過多介紹了,是使用nodeChromium架構的一個桌面端框架,如果有了解使用web技術開發桌面端,這個框架多少會聽說過或者正在使用。

下面就記錄一下自己在閱讀文檔的一些疑問以及開發過程中踩過的坑!

electron?

  1. SpectronDevtron 的作用?

    • Spectron可以和其他的mocha等測試框架進行結合,測試electron
    • Devtron是Electron DevTools的擴展,可以幫助你檢查,監控和調試應用程序。默認是關閉狀態
  2. <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />的作用?

    • 設置 HTTP 的 Content-Security-Policy 頭部字段
    • 爲了防止XSS攻擊,安全考慮可以在content中設置允許加載的腳本的源
  3. electron-forge的作用?

    • 是一個快速構建electron的構建工具,裏面包含打包、自動更新等
    • 內部書寫htmljs爲原生寫法,未集成第三方框架
  4. electron中使用iframewebview的的區別?

    • 官方建議使用iframe代替webview,webview標籤可以加載一個訪客模式的URL頁面,由於這個標籤是基於Chromium webview,目前架構變化較快,不夠穩定。但是webview所提供的功能較多,比如控制訪客能否前進後退等
  5. window.open()使用native和chrome的區別是什麼?

    • electron中使用window.open()打開一個URL使用,會創建一個BrowserWindow實例,使用的是native的窗口,可以通過設置nativeWindowOpen: true來使用chrome內置的window.open(),同步打開窗口
  6. electron-packagerelectron-builder 的打包區別?爲什麼推薦使用 electron-builder

    • electron-packagerelectron-builder 都是用於electron應用打包的模塊, 相比較electron-builder有更豐富的功能,支持更多的平臺,打包的文件更加輕量,支持非electron內置的自動更新(內置的自動更新需上傳到git等支持平臺)
  7. electron中使用node原生模塊的時候,electron-rebuild能夠簡化原生的重編譯?

    • 由於electron內置的node版本和你電腦本機的版本不一致,則在使用node原生模塊的時候可能會報錯,Error: The module '/path/to/native/module.node'
    • 有3中方式可以解決該問題(下面會詳細介紹兩種使用方法,其他方式參考官方文檔)

electron進程之間通訊

  1. 主進程向渲染進程通訊

    • 主進程使用 win.webContents.send 發送消息
    • 渲染進程使用 ipcRenderer.on 接收消息
  2. 渲染進程向主進程通信

    • 渲染進程使用 ipcRenderer.send 或者 ipcRenderer.invoke 發送消息
    • 主進程使用 ipcMain.on或者ipcMain.handle 接收消息
  3. 渲染進程向渲染進程通信

  • 通知事件
    • 通過主進程轉發(Electron 5之前)
    • ipcRenderer.sendTo(Electron 5之後)
  • 數據共享
    • web技術(localStorage、sessionStorage、indexedDB、可嵌入型數據庫等)
    • 使用remote(儘量少用,容易影響性能)

由於公司使用的技術棧是vue,所以需要將electronvue結合起來,當然結合的方式也不麻煩,這裏我們選用了electron-vue這個開源框架。它已經將兩者結合起來,並且能夠獨立打包成web,但在使用過程中還是有些疑問和坑的,記錄一下

electron-vue 的坑與填坑?

  1. 這個框架使用vue-electron模塊,它的作用是什麼?

    • 將 electron API 附加到 Vue 對象,不需要顯式的在vue中引入electron(require('electron')
    • vue-electron源碼很簡單,將 $electron 掛載到vue的原型上
    const electron = require('electron')
    
    module.exports = {
      install: function (Vue) {
        Object.defineProperties(Vue.prototype, {
          $electron: {
            get () {
              return electron
            },
          },
        })
      },
    }
    
    
  2. 這個框架使用vuex-electron模塊,它的作用是什麼?

    • 可以在多個進程中間可以共享狀態,比如在electron的主進程和渲染進程中公用一個狀態,並將數據存放在磁盤中
  3. 在主進程中使用__dirname__filename 打包後,並不能得到我們預期的路徑?

    • 在調用native的dll的時候也遇到這個問題了,打包之前能夠獲得正確路徑,打包後文件會被放在app.asar這個虛擬文件中,路徑就會錯誤
    • 解決方法:electron-vue 中提供了__static全局變量解決了這個問題,只需要將靜態文件放入到__static文件夾即可(path.join(__static, '/xxx.dll')
    • 如果只使用electron本身這個框架,你需要自己設置開發時路徑和打包後的訪問路徑
  4. 初始化完成項目後會報一個錯誤:ReferenceError: process is not defined

    • 可以在index.ejs中將<% if (!process.browser) { %>修改成<% if (!require('process').browser) { %>(如果需要打包成web,請不要使用該方法)
    • 或者在webpack.renderer.config.js修改以下代碼(實際上是配置頁面中使用的process參數,如果需要打包成web,可以使用該方法,但不要在webpack.web.config.js中添加該代碼)
    new HtmlWebpackPlugin({
          filename: 'index.html',
          template: path.resolve(__dirname, '../src/index.ejs'),
    +      templateParameters(compilation, assets, options) {
    +        return {
    +         compilation: compilation,
    +         webpack: compilation.getStats().toJson(),
    +          webpackConfig: compilation.options,
    +          htmlWebpackPlugin: {
    +            files: assets,
    +            options: options
    +          },
    +          process,
    +        };
    +      },
          minify: {
            collapseWhitespace: true,
            removeAttributeQuotes: true,
            removeComments: true
          },
          nodeModules: process.env.NODE_ENV !== 'production'
            ? path.resolve(__dirname, '../node_modules')
            : false
        }),
    
  5. 構建完項目後electron版本默認是2.x, 升級版本後會報錯module is not defined

    • 原因新版本electron默認不開啓node集成,在main/index.js創建窗口的時候加上以下代碼
    mainWindow = new BrowserWindow({
      height: 563,
      useContentSize: true,
      width: 1000,
    +  webPreferences: {
    +    nodeIntegration: true
    +  }
    })
    
  6. 報錯 Unable to install vue-devtools

    • 原因是vue-devtools需要外網下載,科學上網即可
  7. 在該項目中調用vuexmutations時,會報錯Please, don't use direct commit's, use dispatch instead of this.

    • 這裏提示不能使用同步的方法修改,應該通過調用actions去觸發mutations
    • 原因是vuex-electron裏面加載了多進程之間共享mutations插件
    • 解決方法:1.在store/index.js中將createSharedMutations 插件註釋即可
    • 2.通過調用actions去觸發mutations,並且在主進程main/index.js加入該代碼import '../renderer/store'
    • 3.查詢vuex-electrongithub,按照文檔設置whitelist,這裏不贅述
  8. vuex中存儲的數據,即使刷新和重啓項目,該數據依然存在(這裏功能可以設置用戶長期存儲的數據)

    • vuex-electron是將數據存儲在磁盤中,默認路徑可以通過app.getPath('userData')找到存儲路徑下面的一個vuex.json的文件,安裝不同的electron應用,文件目錄下面都會生成一個vuex.json文件
    • vuex-electron內部使用了electron-store這個模塊,這個模塊可以set數據,但是vuex-electron並沒有將暴露出來
    • 解決辦法: 如果你想和在web中一樣,刷新或者關閉後,數據重置,直接在下面第9個問題這,將plugins加載的模塊刪除即可
  9. 使用npm run build:web打包時候,報錯Error: Can't resolve 'fs' in xxxx

    • 修改renderer/store/index.js,在web中去除vuex-electron並刪除顯示引入vuex-electron
    import Vue from 'vue'
    import Vuex from 'vuex'
    - import { createPersistedState, createSharedMutations } from 'vuex-electron'
    import modules from './modules'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      modules,
    +  plugins: process.env.IS_WEB ? [] : [
    +    require('vuex-electron').createPersistedState(),
    +    require('vuex-electron').createSharedMutations()
      ],
      strict: process.env.NODE_ENV !== 'production'
    })
    
  10. 打包報錯

    • 這裏最容易報錯的就是downloading parts=8 size=63 MB url=https://github.com/electron/electron/releases/download/vx.x/electron-vx.x-win32-x64.zip失敗
    • 解決辦法:手動去https://npm.taobao.org/mirrors/electron找到對應electron版本下載
    • 將下載好文件手動放在C:\Users\用戶名\AppData\Local\electron\Cache文件夾中即可(AppData是隱藏文件夾)

electron調用native方法

安裝環境(mac環境無需安裝第1步)

  1. 管理員權限執行npm install --global windows-build-tools 安裝python和C++環境
    如果無法安裝可以單獨安裝
  • python(v2.7 ,3.x不支持)
  • visual C++ Build Tools,或者vs2015及以上
  1. npm install -g node-gyp 編譯原生node模塊(electron使用node原生模塊需要編譯)
  2. node-gyp list 查看是否缺少node.lib 庫,並按照提示安裝 node-gyp install
  3. src/main/index.js中啓用渲染層node集成

通過 node-ffi 模塊調用C/C++ dll

  1. npm install ffi-napi --save 安裝調用C/C++ dll的模塊

    • 如果找不到python路徑,設置python環境變量或者npm config set python你的python路徑
    • 如果還不行嘗試使用yarn安裝
  2. npm install electron-rebuild ---save-dev 安裝自動編譯原生node模塊

    • 如果不安裝的話可以進行手動編譯,方法如下
    • 進入node_module/ffi-napi文件夾執行node-gyp rebuild --target='當前electron的版本' --arch=x64 --dist-url=https://atom.io/download/atom-shell編譯node原生模塊
    • arch : 計算機的架構(x64或者ia32),如果node環境是32位,那麼這裏就是ia32,如果是node環境是64位,那麼這裏就是x64。
    • 如果路徑錯誤,換成國內鏡像路徑 --dist-url=https://npm.taobao.org/mirrors/atom-shell
    • 再進入node_module/ref-napi文件夾執行node-gyp rebuild --target='當前electron的版本' --arch=x64 --dist-url=https://atom.io/download/atom-shell編譯node原生模塊
  3. 如果自動編譯的話執行npx electron-rebuild,上步手動編譯則不需要這一步了

  4. 剩下的就是在主進程裏面調用native方法即可,渲染進程調用通過ipcRendereripcMain通信調用即可,舉個小例子

    // 主進程
    // 調用示例
    const ffi = require('ffi-napi')
    const CTEST =  ffi.Library('dll文件路徑', {
      // 文件內的方法和參數類型
      'Add': ['float', ['float', 'float']],
      'Hello': ['string', []],
      'StrLength': ['int', ['string']]
    })
    // 同步調用
    CTEST.Hello()
    
    // 異步調用
    CTEST.StrLength.async('1234', (error, res) => {
      console.log(error, res)
    })
    

這裏有兩點需要注意一下

  • 如果沒有使用electron-vue這個框架(electron-vue已經處理好靜態文件路徑),需要注意打包後的靜態文件路徑會有些問題,可以在在package.json文件中配置build後dll的文件存放
    "extraFiles": [
        {
        "from": "",
        "to": ""
        }
    ]
    
  • 64位的dll不能再32位機器上調用,會報錯

通過 electron-edge-js 模塊調用C# dll

  1. npm install electron-edge-js --save 安裝調用C# dll的模塊
  2. 這裏electron的版本不能爲8.x,版本7.x的頁面刷新有bug,目前這個模塊支持情況
    * Electron 1.6.x - Node.js v7.4.0.
    * Electron 1.7.x - Node.js v7.9.0.
    * Electron 1.8.x - Node.js v8.2.1.
    * Electron 2.x - Node.js v8.9.3.
    * Electron 3.x - Node.js v10.2.0.
    * Electron 4.0.4+ - Node.js v10.11.0.
    * Electron 5.x - Node.js v12.0.0.
    * Electron 6.x - Node.js v12.4.0.
    * Electron 7.x - Node.js v12.8.1
    
// 主進程
// 調用示例

const edge = require('electron-edge-js')
const edgeDll = edge.func({
  assemblyFile: 'dll文件路徑',
  typeName: "Edge_test.Class1",
  methodName: "Concat"
})

edgeDll({ first: 'aaa', second: 'bb' }, function (error, result) {
  if (error) throw error
  console.log('C# DLL:',result)
})

通過robotjs控制鼠標鍵盤、node-serialport進行串口通訊等

  1. 使用方法這裏不再贅述,文檔查詢即可。這些模塊也是node原生模塊,因此需要編譯
  2. 自動編譯同上npx electron-build,有時候自動編譯不好使,就需要手動編譯
  3. 手動編譯 npm rebuild --runtime=electron --disturl=https://atom.io/download/atom-shell --target=<electron版本> --abi=<對應node版本的abi>或者直接進入模塊文件夾中通過node-gyp編譯:node-gyp rebuild
  4. abi查詢網址:https://github.com/mapbox/node-pre-gyp/blob/master/lib/util/abi_crosswalk.json

自動更新

由於項目代碼是放在自己服務並且不會進行簽名,所以放棄electron內置的自動更新。項目使用的打包方式是electron-builder,最終決定使用electron-updater模塊進行自動更新,它不依賴任何服務器並且可以從S3, GitHub或者任何其它靜態文件存儲更新,避開了 Electron 內置的更新機制

mac上代碼必須要進行簽名

這裏給個簡版示例

  1. npm install electron-updater --save 安裝自動更新模塊
  2. 主進程中寫入更新代碼
    // 每次開啓應用時候會去自動到文件服務器檢查是否有更新,並自動更新重啓
    import { autoUpdater } from 'electron-updater'
    
    autoUpdater.on('update-downloaded', () => {
      autoUpdater.quitAndInstall()
    })
    
    app.on('ready', () => {
      if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
    })
    
  3. 配置打包更新服務器地址(這裏配置從自己文件服務器獲取更新)
    "build": {
      "publish": {
        "provider": "generic",
        "url": "http://127.0.0.1:8080" // 服務器地址
      }
    }
    
  4. 打包後安裝,如果需要更新修改package.json的版本,重新打包,將.exe latest.yml .exe.blockmap 3個文件直接扔在文件服務器地址即可。下次用戶打開應用便會自動更新

在electron中起個node服務

  • 這裏代碼比較簡單,提供個思路:在主進程中創建個子進程執行node服務(child_process.fork),子進程和主進程通過進程之間進行通信,在主進程拿到數據之後通過ipcMainipcRenderer進行主進程和渲染層通信即可。

以上就是個人在閱讀文檔以及項目中遇到的部分問題和坑,記錄分享一下,幫助其他小夥伴少走一些坑,感謝閱讀!

如果文章讓你有收穫,歡迎關注公衆號,不定時推送優質文章!!!

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