Vue進行Electron開發近期增補記錄

繼上一篇文章: 如何用Vue開發Electron桌面程序? 這篇就夠了! - 簡書 (jianshu.com)

這篇文章主要介紹

  • asar包的簡單保護
  • 支持快捷鍵的使用
  • 菜單的動態修改
  • 日志功能

1. asar的包的使用

從上篇我們知道, asar包可以用7z的插件或者直接使用asar命令解壓, 但是有時候我們不像讓人解壓直接看到我們的代碼邏輯, 可以使用一個庫來修改, 即asarmo

image.png

我也有幸貢獻了代碼, 將它的write方法修改成返回Promise對象, 方便進行同步操作, 比如打增量包
feat: make asarmor.write() return the Promise by klren0312 · Pull Request #10 · sleeyax/asarmor (github.com)

const { Asarmor, Trashify, FileCrash } = require('asarmor')
const { join } = require('path')
const AdmZip = require('adm-zip')
const pkg = require('./package.json')
exports.default = async ({ appOutDir, packager, outDir }) => {
  try {
    const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar')
    console.log(`applying asarmor protections to ${asarPath}`)
    const asarmor = new Asarmor(asarPath)
    asarmor.applyProtection(new FileCrash('background.js.LICENSE.txt'))
    asarmor.applyProtection(new Trashify(['.git', '.env']))
    await asarmor.write(asarPath)

    const targetPath = join(appOutDir, './resources')
    const zip = new AdmZip()
    zip.addLocalFolder(targetPath)
    const partUpdateFile = `update-win-${pkg.version}.zip`
    zip.writeZip(join(outDir, partUpdateFile))
  } catch (err) {
    console.error(err)
  }
}

asarmo庫有以下幾個功能(使用7z插件進行解壓, 雖然都會報錯, 但是只有第一種時無法將文件解壓出來, 其他其實都已經解壓出來了)

  1. 對壓縮包中的指定文件進行損壞(一定是不會被調用的文件, 不然會使electron也無法訪問, 導致無法運行)
    image.png
image.png
  1. 生成大量隨機文件填充壓縮包, 解壓的時候阻塞解壓(可以指定文件的體積, 例如10G, 則解壓時會進行10G文件解壓), 但是這樣似乎不會導致文件無法解壓, 取消解壓后, 其實文件已經解壓出來了


    image.png

    image.png

    image.png
  2. 往壓縮包里添加不存在文件


    image.png

    image.png

    image.png

asarmo實現這些的主要原理就是通過chromium-pickle來對asar打包和解包的工具, 對包的header信息進行修改, 從而使解壓出現錯誤

  1. 上面的第一種方法, 我們可以看到, 我們指定的文件size修改成了負值


    image.png
  2. 第二種方法, 我們可以看到, header信息里被添加了很多隨機文件


    image.png
  3. 第三種方法, 我們可以看到, 添加了我們指定的不存在的文件


    image.png

2. 支持快捷鍵的使用

electron官方已經支持了快捷鍵的使用, 可以訪問globalShortcut
了解
我們可以在窗口focus的時候注冊快捷鍵, 然后在blur的時候注銷快捷鍵

// 窗口聚焦
win.on('focus', () => {
    globalShortcut.register('Alt+A', () => {
       // 相關邏輯
    })
})
// 窗口失焦
win.on('blur', () => {
  globalShortcut.unregister('Alt+A')
})

3. 菜單的動態修改

獲取const menuInstance = Menu.buildFromTemplate(this.template)創建的菜單實例
通過menuInstance.items獲取菜單數組, 來修改相關菜單

4. 日志功能

使用winston來實現日志功能,
通過winston-daily-rotate-file: A transport for winston which logs to a rotating file each day. (github.com)對日志進行限制, 定期清除

封裝日志組件

const { transports, createLogger, format } = require('winston')
const { combine, timestamp, printf } = format
require('winston-daily-rotate-file')
const path = require('path')

const dateFormat = date => {
  const addZero = num => {
    if (num < 10) {
      return '0' + num
    } else {
      return num
    }
  }
  const year = date.getFullYear()
  const month = addZero(date.getMonth() + 1)
  const day = addZero(date.getDate())
  const hour = addZero(date.getHours())
  const minute = addZero(date.getMinutes())
  const second = addZero(date.getSeconds())
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}

const myFormat = printf(({ level, message, timestamp }) => {
  return `${dateFormat(new Date(timestamp))} - ${level}: ${message}`
})

const app =
  process.type === 'browser'
    ? require('electron').app
    : require('electron').remote.app

const logDir = path.resolve(app.getPath('userData'), 'logs')

// const logLevel = process.env.NODE_ENV === 'production' ? 'info' : 'debug';
const logLevel = 'debug'

const levels = {
  debug: 0,
  info: 1,
  warn: 2,
  error: 3
}

const d = (level, message) => {
  if (levels[level] >= levels[logLevel]) {
    const consoleLevel = level === 'debug' ? 'log' : level
    console[consoleLevel](message)
  }
}

// App進程
function AppLogger() {
  const appLogFileName = path.resolve(logDir, 'appLogs%DATE%.log')
  const transport = new transports.DailyRotateFile({
    level: logLevel,
    filename: appLogFileName,
    maxSize: '5m',
    maxFiles: '15d,100m' // 15天以前的自動刪除,文件大小超過100m時將舊文件刪
  })

  this.logger = createLogger({
    format: combine(timestamp(), myFormat),
    transports: [transport]
  })
  return this
}

AppLogger.prototype.debug = function debug(message) {
  d('debug', message)
  return this.logger.debug(message)
}

AppLogger.prototype.info = function info(message) {
  d('info', message)
  return this.logger.info(message)
}

AppLogger.prototype.warn = function warn(message) {
  d('warn', message)
  return this.logger.warn(message)
}

AppLogger.prototype.error = function error(message) {
  d('error', message)
  return this.logger.error(message)
}

const appLogger = new AppLogger()

module.exports = {
  appLogger
}

使用日志組件

import { appLogger } from './Logger'
appLogger.info(`------ 日志 ------`)
image.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容