Electron + Vue3 + TS 实战

前言

最近开发了一个新需求 使用 electron 开发桌面应用 整理一下心酸史和一些坑,供大家参考。废话不多说,进入正题

项目构建篇

一、技术要求

二、创建 v3 + ts 项目

 vue create electron-vue3-ts
复制代码
  1. 如下图所示,选择自定义

image.png

  1. 选择一些项目基本配置

image.png

  1. vue版本 这里选择 3.x

image.png

  1. eslint 大家也可以自行选择其他 我这里选择只校验error

image.png

  1. 整体基本配置最终如下

image.png

  1. 开始自动构建

image.png

至此 vue + ts 项目搭建完成

三、v3集成 Electron

目前市面上现有的集成方案有两种:

  1. Vue CLI Plugin Electron Builder
  2. electron-vue

由于 electron-vue 许久未在更新,判定为不在维护,所以技术选择 Vue CLI Plugin Electron Builder

坑一:直接执行vue add electron-builder报错如下 node版本有要求

image.png 正确的操作应该是

确保本地 node version '>=14.0.0'
npm install vue-cli-plugin-electron-builder -D
vue add electron-builder
复制代码

image.png 选择 13 的版本 继续安装 image.png 安装完成以后项目整体目录结构如下:

image.png

安装完成以后 发现控制台输出一个警告

 WARN  
 It is detected that you are using Vue Router. 
 It must function in hash mode to work in Electron. 
 Learn more at https://goo.gl/GM1xZG
复制代码

大致意思就是 在electron中 vue 只能使用 hash 模式。history 模式不支持 修改如下

 import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router'
 const router = createRouter({
  history: process.env.IS_ELECTRON ? createWebHashHistory(process.env.BASE_URL) : createWebHistory(process.env.BASE_URL),
  routes
})
复制代码

image.png 修改完毕后,启动项目

 npm run electron:serve
复制代码

image.png 启动过程总会发现 需要等待好长一段时间项目才能起来 这是因为installExtension方法会持续请求安装VUEJS3_DEVTOOLS,然后导致项目启动非常慢,必须等它五次请求超时后,才能启动项目 image.png 所以可以把这一段代码注释掉不执行

// background.ts
// 第一部分,这行代码注释掉
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
// 第二部分,这几行代码注释掉
if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installExtension(VUEJS3_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
}
复制代码

image.png 至此 electron + v3 + ts 项目就搭建成功了

项目实战篇

image.png

如图,整体项目结构基本没什么大的改变,基本按照正常vue 项目开发就可以了,只不过需要了解一下electron里面主进程和渲染进程

一、Electron中的主进程和渲染进程

什么是主进程

  • electron项目启动的时候运行main.js的进程就是主进程
  • 一个项目有且只有一个主进程
  • 创建窗口等所有系统事件都要在主进程中进行
    简单的说就是我们的electron项目的主进程只有一个, 主进程的执行代码需要写到main.js中, 所有跟系统事件相关的代码统统都要写在这里

什么是渲染进程

  • 我们每创建一个web页面都会创建一个渲染进程
  • 每个web页面运行在它自己的渲染进程中
  • 每个渲染进程是独立的, 它只关心它所运行的页面
    简单理解就是一个web页面一个渲染进程

主进程和渲染进程的区别

  • 主进程通过BrowserWindow创建页面
  • 每个BrowserWindow实例都在自己的渲染进程中运行, 当BrowserWindow实例被销毁后, 相应的渲染进程也会被终止

主进程和渲染进程怎么通信

const { ipcMain } = require('electron')
// 监听渲染进程发送的消息
ipcMain.on('renderer-msg', (event, arg) => {
  console.log(arg) // prints '帮我创建一个新的页面'
  event.reply('main-msg', '好的');  // 给渲染进程回复消息
})
复制代码
  • 渲染进程代码
const { ipcRenderer } = require('electron')
// 监听主进程发送的消息
ipcRenderer.on('main-msg', (event, arg) => {
  console.log(arg) // prints '好的'
})
// 给主进程发消息
ipcRenderer.send('renderer-msg', '帮我创建一个新的页面')
复制代码

更多关于 electron 的知识 可以查看官方文档 基本都能找到答案

坑二:在渲染进程中使用electron 直接在vue页面里面引用会报错

image.png

Uncaught ReferenceError: __dirname is not defined
复制代码

此时百度一搜 大部分给出的结局方案就是 在项目根目录中创建vue.config.js文件,添加以下内容

module.exports = {
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true
    }
  }
}
复制代码

添加配置之后 再重新运行项目,发现问题解决了 但是这个时候如果直接在浏览器打开访问的话你会发现 项目还是报错 image.png 在electron中并没有问题 image.png 另一种解决方案就是使用preload预加载脚本

二、Electron preload预加载

来看看electron 官方介绍

  • preload String (可选) -在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入node的全局引用标志
  • preload是BrowserWindow类的参数webPreferences的一个可选设置项,preload脚本的加载时机就是窗口建立后,页面加载之前。若是有人问,若是不挪用loadURL方式,不加载页面,preload脚本会加载吗?谜底是会,但有什么用呢?你起个壳子不给人家看页面是什么鬼?不管这些,主要的是我们明白这个加载时机就好了。
  • 然后还要清楚一点,preload脚本是运行在渲染进程中的。再有一点就是,preload脚本中可以调用window工具(渲染进程其实就是起了个浏览器壳子),preload脚本运行在渲染进程,提前于页面和其他所有js的加载,又能调用Node API;脚本文件路径为绝对路径,当node integration关闭时,预加载的脚本将从全局范围重新引入node的全局引用标志:联系前面两点明白就好了。

那么,到底什么是预加载?
预加载就是:某一个渲染进程,在页面加载之前加载一个本地脚本,这个脚本能调用所有Node API、能调用window工具。用法如下:

const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
        preload: path.join(__dirname, 'preload.js')
    }
})
复制代码

知道了什么是preload 那么咱们回到上一个问题 如何使用preload解决报错

  • 首先在 src 目录下创建一个preload.ts文件
preload.ts
import { ipcRenderer } from 'electron'
(window as any).ipcRenderer = ipcRenderer
复制代码
  • 然后在vue.config.js配置加载preload.ts
module.exports = {
  pluginOptions: {
    electronBuilder: {
      // nodeIntegration: true,
      preload: 'src/preload.ts'
    }
  }
}
复制代码
  • 然后在background.ts配置加载preload.js
const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: (process.env.ELECTRON_NODE_INTEGRATION as unknown) as boolean,
      contextIsolation: !!process.env.ELECTRON_NODE_INTEGRATION,
      enableRemoteModule: true,
      webSecurity: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })
复制代码
  • 最后在vue 文件中直接使用就可以了
setup() {
    const ipcRenderer = (window as any).ipcRenderer
    // 监听主进程发送的消息
    ipcRenderer.on('main-msg', (event: any, arg: string) => {
      console.log(arg) // prints '好的'
    })
    // 给主进程发消息
    ipcRenderer.send('renderer-msg', '帮我创建一个新的页面')
}
复制代码

此时在浏览器打开访问的话你会发现 页面有东西了 但是js还是报错 image.png 在electron中并没有问题 image.png 至于如何解决在浏览器打开访问报错问题 我一时半会也没有找到什么别的好办法 欢迎大家在评论区留言

三、Electron打包配置

vue 已经把 electron-builder 集成进来了 所以只需要在vue.config.js里面配置就可以了

module.exports = {
  pluginOptions: {
    electronBuilder: {
      // nodeIntegration: true,
      preload: 'src/preload.ts',
      builderOptions: {
        productName: "", // 项目名,也是生成exe文件的前缀名
        appId: "", // 包名e7
        copyright: "", // 版权信息
        compression: "store", // "store" | "normal" | "maximum" 打包压缩情况(store 相对较快),store 39749kb, maximum 39186kb
        directories: {
          output: "dist_electron" // 输出文件夹
        },
        asar: true, // asar打包
        win: {
          icon: "build/icons/icon.ico", // 图标路径
          target: [
            {
              target: "nsis", // 安装应用
              arch: [
                // ia32 | x64 | armv7l | arm64
                "ia32", // 即–arch=ia32, 32位操作系统,也可以在64位操作系统中安装
                "x64" // 即–arch=x64, 64位操作系统,使用本架构打包无法再32位操作系统中安装
              ]
            }
          ]
        },
        linux: {
          icon: "build/icons",
          category: "home",
          target: [
            {
              target: "AppImage", // AppImage, snap, deb, rpm, freebsd, pacman, p5p, apk, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir.
              arch: ["arm64"]
            }
          ]
        },
        mac: {
          icon: "build/icons/icon.icns",
          target: [
            {
              target: "dmg"
            }
          ]
        },
        nsis: {
          oneClick: false, // 一键安装
          // "guid": "xxxx", //注册表名字,不推荐修改
          perMachine: false, // 是否开启安装时权限限制(此电脑或当前用户)
          allowElevation: true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
          allowToChangeInstallationDirectory: true, // 允许修改安装目录
          installerIcon: "build/icons/icon.ico", // 安装图标
          uninstallerIcon: "build/icons/icon.ico", //卸载图标
          installerHeaderIcon: "build/icons/icon.ico", // 安装时头部图标
          createDesktopShortcut: true, // 创建桌面图标
          createStartMenuShortcut: true, // 创建开始菜单图标
          shortcutName: "" // 图标名称
        }
      }
    }
  }
}
复制代码

mac图标生成 icns

  • 准备一张 1024 * 1024 的png图片,命名为 icon.png
  • 创建一个用来存放不同大小图片的临时目录 命名为 tmp.iconset
 mkdir tmp.iconset
复制代码
  • icon.png转为不同大小的图片,并放入tmp.iconset
sips -z 16 16     icon.png --out tmp.iconset/icon_16x16.png
sips -z 32 32     icon.png --out tmp.iconset/[email protected]
sips -z 32 32     icon.png --out tmp.iconset/icon_32x32.png
sips -z 64 64     icon.png --out tmp.iconset/[email protected]
sips -z 128 128   icon.png --out tmp.iconset/icon_128x128.png
sips -z 256 256   icon.png --out tmp.iconset/[email protected]
sips -z 256 256   icon.png --out tmp.iconset/icon_256x256.png
sips -z 512 512   icon.png --out tmp.iconset/[email protected]
sips -z 512 512   icon.png --out tmp.iconset/icon_512x512.png
sips -z 1024 1024   icon.png --out tmp.iconset/[email protected]
复制代码
  • 通过iconutil生成icns文件
 conutil -c icns tmp.iconset -o Icon.icns
复制代码

此时就得到了icon.icns Apple图标图像
执行打包命令

npm run electron:build
复制代码

image.png

window图标生成 ico

坑三 切记不要直接将.png结尾的图标直接改成.ico打包的时候会报错

image.png 通过ico在线生成工具去生成一个就可以了 尺寸选择 256*256 image.png
再重新打包就可以了

npm run electron:build_win
复制代码

image.png

备注:本文是以mac开发环境进行的打包 可以同时打mac包和window包;如果是window本进行打包 只能打window包 相应的打包命令就是npm run electron:build

项目git地址

github.com/jifenjilian…
推荐使用 npm 安装 根目录创建.npmrc文件 使用taobao源进行依赖安装

electron_mirror=https://npm.taobao.org/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/
phantomjs_cdnurl=http://npm.taobao.org/mirrors/phantomjs/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
复制代码

总结

到此 electron + vue3 从项目搭建开始、到项目开发、再到最后的项目打包 整体流程基本就梳理出来了,相信看到这的小伙伴一定会有所收获。文中的不同之处,也欢迎大家在评论区留言讨论。

参考

猜你喜欢

转载自juejin.im/post/7037044758342008839