Vue进行Electron开发近期增补记录

2021-05-06 16:24:25 浏览数 (1)

继上一篇文章: 如何用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)

代码语言:javascript复制
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

  1. 往压缩包里添加不存在文件

image.png

image.png

image.png

asarmo实现这些的主要原理就是通过chromium-pickle来对asar打包和解包的工具, 对包的header信息进行修改, 从而使解压出现错误

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

image.png

  1. 第二种方法, 我们可以看到, header信息里被添加了很多随机文件

image.png

  1. 第三种方法, 我们可以看到, 添加了我们指定的不存在的文件

image.png

2. 支持快捷键的使用

electron官方已经支持了快捷键的使用, 可以访问globalShortcut

了解

我们可以在窗口focus的时候注册快捷键, 然后在blur的时候注销快捷键

代码语言:javascript复制
// 窗口聚焦
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)对日志进行限制, 定期清除

封装日志组件

代码语言:javascript复制
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�TE%.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
}

使用日志组件

代码语言:javascript复制
import { appLogger } from './Logger'
appLogger.info(`------ 日志 ------`)

image.png

0 人点赞