Electron+VueCli构建桌面app

electron+vue-cli学习笔记

[TOC]

说在前面

  1. 本人也是纯小白一个,接触Electron也不久。这篇文章主要是用来记录笔记,有些东西长时间不用就容易忘记,这里做好笔记在之后也会节省很多时间。另外也想分享一下自己的学习过程还有一些踩过的坑,希望帮助到有需要的人!

  2. 本篇文章面向有一定前端基础的小白(HTML、CSS、JS…),vue2和vue-cli,其实vue倒是可有可无,如果你只想创建一个简单的electron应用程序,有前端三件套的知识就够了。文章主要介绍electron的基础使用、electron的打包、以及在vue-cli开发环境中如何使用electron去打包出一个桌面端应用程序(win & Mac)。

  3. 所用到的语言以及工具。

  4. Electron是什么?
    Copy一段来自官网的介绍:

    Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。

基础Electron部分

**注意:如果你只想看vue-cli篇,请自行跳转到Vue-cli篇,这部分安装的electron与vue-cli篇部分安装的不一样。

安装node.js

正如Electron官网所说:在使用Electron进行开发之前,您需要安装**Node.js。**

在node.js的环境中自带有npm,在之后的开发插件安装中,几乎都离不开npm来安装一些依赖和库(也可以使用yarn和pnpm…)

1. 下载node.js安装包(自行安装v14.17.5以上版本)

2. 配置Node.js环境变量

  • 将安装的目录配置到系统环境变量和用户变量中

3. 测试Node.js是否安装成功

cmd或者vscode终端中输入以下命令

1
2
node -v    #查看node版本
npm -v #查看npm版本

如果显示node和npm版本这表示安装Node.js以及配置环境变量成功!

安装Electron

在安装好Node.js的前提下,接下来我们来安装 Electron

1. 初始化一个Node.js项目

在安装electron之前我们需要先创建一个文件夹,并初始化它为一个新的Node.js项目。

  • 首先我们创建一个文件夹

  • 使用cmd或者vscode终端进入该文件夹

  • 输入一下命令将文件夹初始化为一个Node.js项目

    1
    npm init
  • Enter在输入了描述、作者等信息后,这时候你会发现你的文件夹中出现了package.json文件

  • package.json文件大致是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "name": "my-electron-test",
    "version": "1.0.0",
    "description": "xxxx",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    },
    "author": "VGhunter",
    "license": "ISC",
    "dependencies": {
    }
    }

    这个文件里面的参数我们后面会一点点添加和修改。
    到这里你成功初始化了一个Node.js项目文件。

2. 使用npm命令安装Electron

好了,现在可以安装Electron了。

  • cmd或者vscode终端下输入一下命令

    1
    2
    npm install electron --save-dev   #在当前项目中安装electron
    npm install -g electron #在全局安装electron

    注意:这里本人建议使用全局安装,因为这里有一个大坑。

    在安装过程中,electron 模块会通过 electron-download 为您的平台下载 Electron 的预编译二进制文件。

    这个是官方原话。没错,它会在安装之后下载所谓的预编译二进制文件,这点我问过chatGPT,它回答我说,这里的预编译文件大概80m左右,但是它真的要下特别特别久,没有耐心的朋友可能就中途就退出了。所以建议大家全局安装,就不用每次安装了。这一点我也不是特别理解是什么东西。如果有朋友们有什么解决办法,或者更好的办法麻烦可以评论区说一下。

  • 下载完成之后 测试一下是否安装成功了

    1
    2
    npm list --depth=0       //查看已经安装的项目依赖
    npm list -g --depth=0 //查看已经安装的全局依赖

    如果都看到了electron@22.0.0则代表安装成功了
    这时在看你的项目文件夹,发现多了一个node_modules文件夹,不用管他,里面就装着一些相关依赖啦!
    再看我们的package.json文件:
    如果你是使用npm install electron --save-dev安装的话,你的文件里面应该多出了一个devDependencies

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "name": "my-electron-test",
    "version": "1.0.0",
    "description": "xxxx",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    },
    "author": "VGhunter",
    "license": "ISC",
    "dependencies": {
    },
    "devDependencies": {
    "electron": "^22.0.0", //刚刚安装的electron依赖显示在这
    },
    }

    如果你使用的npm install -g electron,那么electron就安装在全局,这样在任何项目都可以使用electron啦!

创建一个Electron应用

1. 编辑package.json文件

  • 在package.json中添加main程序入口和scripts程序启动命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "name": "my-electron-test",
    "version": "1.0.0",
    "description": "xxxx",
    "main": "main.js", //Electron程序入口文件
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "electron ." //Electron程序启动命令
    },
    "author": "VGhunter",
    "license": "ISC",
    "dependencies": {
    }
    }

2. 创建main.js入口文件

  • 在文件根目录创建main.js文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // main.js
    // 引入app和BrowserWindow
    const { app, BrowserWindow } = require('electron')
    const path = require('path')

    const createWindow = () => {
    // 创建一个窗口,长为800宽为600
    const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: { // 预加载脚本
    preload: path.join(__dirname, 'preload.js')
    }
    })

    // 加载 index.html,就是你想展示的界面也可以使用
    mainWindow.loadFile('index.html')

    // 打开开发工具
    // mainWindow.webContents.openDevTools()
    }

    // 这段程序将会在 Electron 结束初始化
    // 和创建浏览器窗口的时候调用
    // 部分 API 在 ready 事件触发后才能使用。
    app.whenReady().then(() => {
    createWindow()

    app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })
    })

    // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。
    app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') app.quit()
    })
    // code. 也可以拆分成几个文件,然后用 require 导入。

3. 创建index.html文件

  • 在根目录创建你想暂时的index.html文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--index.html-->
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
    <title>Hello World!</title>
    </head>
    <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.

    <!-- 您也可以此进程中运行其他文件 -->
    <script src="./renderer.js"></script>
    </body>
    </html>

    这个时候你在终端输入npm run start,就可以启动Electron程序了
    不出意外应该会弹出一个窗口,而窗口的内容就是你的index.html中编写的内容

4. 创建预加载脚本preload.js(可有可无)

  • 同样在项目根目录创建一个名为preload.js的文件 作为预加载脚本文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
    }

    for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
    }
    })

    预加载脚本在渲染器进程加载之前加载,并有权访问两个 渲染器全局 (例如 window 和 document) 和 Node.js 环境。

    这个文件的代码大概作用就是在程序加载之前,通过这个文件去修改index.html的DOM元素,将版本号输出到index.html中,具体的我也不是很懂。总之这个文件如果不创建也是不影响的!!

  • 到了这里,一个超级基础的Electron应用程序就算创建完成了。
    使用npm run start来启动你的程序,你见看到一个windows窗口弹出,而里面是你创建index页面。
    如果你想让你的应用变得更加丰富,请修改你的main.js文件,来让你的应用程序有更完善的功能。
    更多的教程还请浏览**Electron官网开发指南**

5. 安装nodemon实时刷新插件

到这里你已经已经可以使用npm run start去开启你的Electron程序了
但是我们来优化一个在开发中遇到的问题
每次我们使用npm run start打开应用查看之后,如果我们修改了内容,页面不会实时刷新,需要Ctrl+C关闭程序在重新启动!
我们优化这个问题,使用 nodemon

  • 在全局安装nodemon

    1
    npm install -g nodemon
  • 修改Electron启动命令
    进入根目录的package.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "name": "my-electron-test",
    "version": "1.0.0",
    "description": "xxxx",
    "main": "main.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --exec electron ." //添加nodemon启动方式
    },
    "author": "VGhunter",
    "license": "ISC",
    "dependencies": {
    "nodemon": "^2.0.20"
    }
    }

    现在你再使用npm run start启动,然后修改main.js文件的内容,程序将自动重启刷新内容

Electron + Vue-Cli部分

  • 知道了基础的Electron使用方法之后,我们开始结合Vue-Cli来创建一个Electron应用。
  • 这里我们使用的插件是 Vue CLI Plugin Electron Builder 来实现在脚手架创建Electron应用
  • 此外你还可以使用Electron-vue脚手架来直接创建一个基于vue的electon项目

1. 初始化一个vue-cli项目

  • 安装vue-cli脚手架

    1
    2
    3
    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli

    安装完成之后 你可以在终端使用vue --version查看是否安装成功

  • 创建一个vue-cli项目
    切换到你想创建项目的目录 执行一下指令

    1
    vue create my-vue-cli

    选择你需要创建的vue版本,这里我们使用vue2来举例

  • 启动vue-cli项目
    切换到你创建的vue脚手架项目内部 使用一下命令启动服务

    1
    npm run serve

    在加载完成之后 默认情况使用https://localhost:8080就能看到界面了
    这样就创建好了一个vue脚手架项目,具体的vue-cli脚手架使用方法,还请浏览Vue CLI官网

2. 安装Vue CLI Plugin Electron Builder

  • Vue CLI Plugin Electron Builder是一个基于Vue的插件 所以我们使用一下命令来安装

    1
    vue add electron-builder

    稍等片刻后…
    额没错,你会发现等了很久,因为使用这个命令安装同样是需要下载electron-13版本的二进制预编译包的,现在我也没有找到其他办法,貌似只有等待。但是好像在下载了一遍之后,在其他新建的项目中就不会再下载了,可能是缓存到了本地,对此我也不是很懂,如果有懂的大佬麻烦评论区留言,谢谢!!

  • 漫长的等待之后 我们来查看一下package.json文件
    如果你的package.json文件和这个差不多 而且安装没有报错 就算是安装成功了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    {
    "name": "electron-app",
    "version": "0.1.0",
    "description": "electron-app",
    "author": "VGhunter",
    "private": true,
    "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps",
    },
    "main": "background.js",
    "dependencies": {
    "core-js": "^3.8.3",
    "electron-icon-builder": "^2.0.1",
    "vue": "^2.6.14"
    },
    "devDependencies": {
    "@babel/core": "^7.12.16",
    "@babel/eslint-parser": "^7.12.16",
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-eslint": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "electron": "13.0.0",
    "electron-devtools-installer": "^3.1.0",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^8.0.3",
    "nodemon": "^2.0.20",
    "vue-cli-plugin-electron-builder": "^2.1.1",
    "vue-template-compiler": "^2.6.14"
    },
    "eslintConfig": {
    "root": true,
    "env": {
    "node": true
    },
    "extends": [
    "plugin:vue/essential",
    "eslint:recommended"
    ],
    "parserOptions": {
    "parser": "@babel/eslint-parser"
    },
    "rules": {}
    },
    "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
    ]
    }
  • 我们使用npm run electron:serve来看看是否能够启动应用

    1
    npm run electron:serve

    这个命令会自动先启动一遍vue-cli的服务在在启动electron服务
    如果启动成功 则会出现一个window窗口 里面就是你的vue界面 代表插件安装正常

3. 程序入口background.js文件

  • 在vue-electron-builder插件中,我们的electron的入口文件被放在src目录中 名为background.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
      'use strict'

    import { app, protocol, BrowserWindow } from 'electron'
    import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
    import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
    const isDevelopment = process.env.NODE_ENV !== 'production'

    // Scheme must be registered before the app is ready
    protocol.registerSchemesAsPrivileged([
    { scheme: 'app', privileges: { secure: true, standard: true } }
    ])

    async function createWindow() {
    // Create the browser window.
    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,
    contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
    }
    })

    if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
    } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
    win.removeMenu()
    }
    }

    // Quit when all windows are closed.
    app.on('window-all-closed', () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
    app.quit()
    }
    })

    app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
    })

    // This method will be called when Electron has finished
    // initialization and is ready to create browser windows.
    // Some APIs can only be used after this event occurs.
    app.on('ready', async () => {
    if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
    await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
    console.error('Vue Devtools failed to install:', e.toString())
    }
    }
    createWindow()
    })

    // Exit cleanly on request from parent process in development mode.
    if (isDevelopment) {
    if (process.platform === 'win32') {
    process.on('message', (data) => {
    if (data === 'graceful-exit') {
    app.quit()
    }
    })
    } else {
    process.on('SIGTERM', () => {
    app.quit()
    })
    }
    }
  • 这里主要的逻辑写在createWindow()这个函数中 其中BrowserWindow({...})是创建一个windows窗口,你也可以自行new一个新的窗口在合适的时候弹出。

  • widthheight分别是设置窗口的长和宽,这里还有一些简单的属性,比如:

    1. show:true 是否在程序启动时显示这个窗口。
    2. frame:false是否显示窗口边框
    3. alwaysOnTop:true窗口是否总是显示在最前面

      更多的请浏览Electron官网API-BrowserWindow
  • if (process.env.WEBPACK_DEV_SERVER_URL)模块中,我们对窗口执行一些操作,比如win.loadRUL(url)是将url连接加载到win这个窗口中,而上面代码就是加载vue-cli服务的https://loaclhost:8080这个地址。使用win.loadFile(path)则是使用文件路径加载文件。

    注意:关于这个地方有个坑:请跳 这里

  • app.on('window-all-closed', () => {...}意思是在所有窗口都关闭的时候,关闭程序。下面是几个简单常用的:

    1. win.show()展示窗口并聚焦到顶部
    2. win.destroy()销毁窗口
    3. win.focus()聚焦窗口
  • 其他还有更多内容请浏览 Electron官网 应用程序开发Api

4. 在窗口加载本地html文件

  • 由于在服务启动之后,你没有办法直接用路径去加载一个本地文件。
    这里vue-electron-builder给出了一个全局变量__static,我们使用path.json方法创建一个路径,方法如下:

    1. 将你想加载的html文件放到public文件目录中.
    2. 使用path.json来创建路径,并使用win.loadFile()来加载html文件。
    1
    2
    const fileLocation = path.join(__static, 'preload.html')
    win.loadFile(fileLocation);

    这样 你就能在background.js中创建一个加载本地文件的窗口了…

5. 去除窗口默认菜单栏

  • 在一个应用程序项目中 我们不可能去使用electron的默认菜单栏 怎么去除呢?有下面几种方法:

    1. 引入Menu方法,在app.on('ready',()=>{...})中添加Menu.setApplicationMenu(null)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    app.on('ready', async () => {
    if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
    await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
    console.error('Vue Devtools failed to install:', e.toString())
    }
    }
    createWindow()
    Menu.setApplicationMenu(null); //去除默认菜单栏
    })
    1. 使用win.setMenu(null);
    1
    2
    3
    app.on('browser-window-created', function (event, window) {
    window.setMenu(null);
    });

    也可以直接在createWindow()函数对所需要操作的窗口使用。

    1. 使用win.removeMenu(),直接在createWindow()函数中对所需要的窗口使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ...
    const load = new BrowserWindow({
    width: 550,
    height: 300,
    show:true,
    frame:false,
    // resizable: true //用于可调整窗口大小(貌似没用)
    alwaysOnTop:true,
    })
    ...
    load.removeMenu() //移除load窗口的默认菜单栏

    当然如果你先创建自己的菜单栏也是可以的,请浏览 Menu

6. 打包应用程序

注意:在打包之前这里要讲一个我自己遇到的坑!在background.js文件夹中,我们在开发阶段测试时的代码都时写在if (process.env.WEBPACK_DEV_SERVER_URL) {...}中的,这时如果你打包,会发现写的任何逻辑都无效了。因为这个模块里面是写开发时的逻辑的,而如果不是开发时的代码要写在下面的 else模块中!!巨坑呀,我也是没有注意到有英文提示,整了我好久…没想到是写错位置了/(ㄒoㄒ)/~~

  • 使用npm run electron:build来将项目打包成桌面程序
    打包成功之后 根目录会出现新的文件夹dist_electron,里面就是打包好的应用程序。

  • 修改打包设置,图标、安装路径等…

    1. 先将图片裁剪成正确的大小和格式,使用 electron-icon-builder
      安装插件:

      1
      npm install -g electron-icon-builder

      package.json文件中的script中添加命令:

      1
      "build-icon": "electron-icon-builder --input=./public/logo.jpg --output=build --flatten"

      其中--input为你需要裁剪的图片路径,--output为你的图片出路径
      使用插件:

      1
      npm run build-icon
    2. 在根目录原有的vue.config.js 文件中添加配置:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      const { defineConfig } = require('@vue/cli-service')
      module.exports = defineConfig({
      transpileDependencies: true,
      pluginOptions: {
      electronBuilder: {
      builderOptions: {
      appId: "184789",
      productName: "electronApp", //项目名,也是生成的安装文件名,即aDemo.exe
      copyright: "nicnic © 2022", //版权信息
      directories: {
      output: "./dist" //输出文件路径
      },
      win: {
      //win相关配置
      icon: "./build/icons/icon.ico", //图标,当前图标在根目录下,注意这里有两个坑
      target: [
      {
      target: "nsis", //利用nsis制作安装程序,打包文件的后缀为exe
      arch: [
      "x64", //64位
      "ia32" //32位
      ]
      }
      ]
      },
      nsis: {
      oneClick: false, //一键安装
      language: "2052", //安装语言
      perMachine: true, //应用所有用户
      allowToChangeInstallationDirectory: true //用户可以选择路径
      }
      }
      }
      }
      })

      这时候我们再使用npm run electron:build,就会dist文件夹中出现已经打包好的应用程序文件夹和应哟安装程序了,而且图标和选择安装路径功能都实现了!
      更多的配置请自行研究electron-builder

最后

到这里关于electron+vue-cli的简单分享就完成了,因为第一次认真写文章,废话也有点多,还请见谅!如果你也成功的创建了一个electron应用,请留言评论告诉我吧,如果在文章中有哪里有问题,欢迎到评论区提出!!!


Electron+VueCli构建桌面app
http://example.com/2023/01/18/Electron-VueCli构建桌面app/
作者
Mr.H
发布于
2023年1月18日
许可协议