Electron集成Vue Cli3创建项目

2020-05-09 14:54:19 浏览数 (1)

Vue CLi3环境配置

卸载旧版本

代码语言:javascript复制
npm uninstall vue-cli -g# OR yarn global remove vue-cli

安装新版本

代码语言:javascript复制
npm install -g @vue/cli# ORyarn global add @vue/cli

检查其版本是否正确 (3.x)

代码语言:javascript复制
vue --version

创建项目后添加依赖

代码语言:javascript复制
vue ui

安装插件

vue-cli-plugin-electron-builder

插件官网地址: https://nklayman.github.io/vue-cli-plugin-electron-builder/

选择Electron版本为5.0.0

Electron5.06.0的语法变化不大 选用5.0是因为node-ffi第三方修改版也只能支持到5.0

运行报错

INFO Launching Electron… Failed to fetch extension, trying 4 more times Failed to fetch extension, trying 3 more times Failed to fetch extension, trying 2 more times Failed to fetch extension, trying 1 more times Failed to fetch extension, trying 0 more times Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT

注释掉src/background.js中的以下代码就行了

代码语言:javascript复制
if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
        await installVueDevtools();
    } catch (e) {
        console.error("Vue Devtools failed to install:", e.toString());
    }
}

虽然还是会报错 但是不用等它尝试下载那么多次了 不用管这个错误即可

旧项目添加依赖

开发依赖添加

代码语言:javascript复制
"devDependencies": {
	"electron": "5.0.0",
	"vue-cli-plugin-electron-builder": "^1.4.0"
}

运行脚本添加

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

入口变成了background.js

在src目录下创建background.js

代码语言:javascript复制
"use strict";

import { app, protocol, BrowserWindow } from "electron";
import {
  createProtocol,
  installVueDevtools
} from "vue-cli-plugin-electron-builder/lib";
const isDevelopment = process.env.NODE_ENV !== "production";

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;

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

function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    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.on("closed", () => {
    win = null;
  });
}

// 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 (win === null) {
    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 installVueDevtools();
    } 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();
    });
  }
}

就项目中的Electron中的静态页面建议放在public文件夹中

打包配置

我们使用的vue-cli-plugin-electron-builder内部也是用electron-builder打包的,但是配置的位置不能像之前那样配置了

官方:https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/configuration.html

旧配置

之前的配置 build在根节点上

代码语言:javascript复制
"build": {
    "appId": "cn.psvmc",
    "productName": "互动课堂",
    "icon":"build/app.ico",
    "files": [
      "**/*",
      "static/*"
    ],
    "asar": true,
    "mac": {
      "icon":"build/app.ico",
      "target": [
        "dmg",
        "zip"
      ]
    },
    "win": {
      "icon":"build/app.ico",
      "target": [
        "zip"
      ]
    },
    "nsis": {
      "oneClick": false,
      "allowElevation": true,
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "build/app.ico",
      "uninstallerIcon": "build/app.ico",
      "installerHeaderIcon": "build/app.ico",
      "createDesktopShortcut": true,
      "createStartMenuShortcut": true,
      "license": "LICENSE.txt"
    }
  },

新配置

在项目的根目录中的vue.config.js中添加以下配置

如果没有该文件的话创建即可

代码语言:javascript复制
module.exports = {
  pluginOptions: {
    electronBuilder: {
      builderOptions: {
        appId: "cn.psvmc",
        productName: "星火智慧课堂",
        icon: "./app.ico",
        files: ["**/*", "static/*"],
        asar: true,
        mac: {
          icon: "./app.ico",
          target: ["zip", "dmg"]
        },
        win: {
          icon: "./app.ico",
          target: ["zip", "nsis"]
        },
        nsis: {
          oneClick: false,
          allowElevation: true,
          allowToChangeInstallationDirectory: true,
          installerIcon: "./app.ico",
          uninstallerIcon: "./app.ico",
          installerHeaderIcon: "./app.ico",
          createDesktopShortcut: true,
          createStartMenuShortcut: true,
          license: "./LICENSE.txt"
        }
      }
    }
  }
};

注意事项

  • 图标的路径是相对于vue.config.js所在目录的相对位置,也就是说图标要放在项目的根目录,不是构建生成后目录的路径。
  • LICENSE.txt文件的编码必须为GBK编码

页面加载方式

之前直接用Electron写的代码,后来要结合Vue Cli3创建的项目,本来想的是直接把Electron的代码放在Vue的public目录中,加载的时候用下面的方式

代码语言:javascript复制
if (process.env.WEBPACK_DEV_SERVER_URL) {
    // 开发环境
    win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
    pptWindow.loadFile("../public/classtools/ppt/ppt.html");
} else {
    // 正式发布
    createProtocol("app");
    win.loadURL("app://./index.html");
    pptWindow.loadURL("app://./classtools/ppt/ppt.html");
}

但是发现开发环境中完全没问题,打包后就各种找不到依赖

所以这种方式是行不通的,只能把Electron中的静态页面用Vue的方式在写一遍

但是问题是Electron中用的Node,包导入导出用的CommonJS规范,而Vue用的是ES6的规范,所以代码也要微改。

原写法

代码语言:javascript复制
exports.mydb = mydb;

改为

代码语言:javascript复制
export default mydb;

加载页面方式改为

代码语言:javascript复制
if (process.env.WEBPACK_DEV_SERVER_URL) {
    // 根页面
    win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
    // 内页
    toolbarWindows.loadURL(
        process.env.WEBPACK_DEV_SERVER_URL   "#toolbar"
    );
} else {
    createProtocol("app");
    // 根页面
    win.loadURL("app://./index.html");
    // 内页
    toolbarWindows.loadURL("app://./index.html#toolbar");
}

loadFile全部改为了loadURL

调用二进制文件(FFMPEG)

Electron默认是开启asar的,导致二进制文件也被打在了asar文件中,就不能再被调用,所以我们要让二进制文件不被处理。

首先我们要知道程序的打包步骤

webpack打包 => electron-builder打包asar打包 => exe打包

考虑到不同平台需要打入的ffmpeg不同,我们可以在webpack打包过程中筛选使用的文件,过程如下:

如果软件不考虑多平台,那么可以直接下载对应平台的ffmpeg放在项目根目录下的core文件夹中,下面的第一步可以跳过不用配置。

第一步

安装ffmpeg-staticcopy-webpack-plugin

代码语言:javascript复制
npm install --save-dev ffmpeg-static
npm install --save-dev copy-webpack-plugin

配置vue.config.js

把对应平台的文件放在项目根目录的core文件夹中

代码语言:javascript复制
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
  outputDir: "webapp",
  assetsDir: "assets",
  filenameHashing: false,
  lintOnSave: true,
  productionSourceMap: false,
  configureWebpack: config => {
    const plugins = [];
    // 打包不同平台的 ffmpeg 到 app
    const ffmpegBasePath = "node_modules/ffmpeg-static/bin/"; // ffmpeg-static
    const { platform } = process;
    const ffmpegPathMap = {
      darwin: "darwin/x64/ffmpeg",
      win32: "win32/ia32/ffmpeg.exe",
      win64: "win32/x64/ffmpeg.exe",
      linux32: "linux/ia32/ffmpeg",
      linux64: "linux/x64/ffmpeg"
    };
    const ffmpegPath = ffmpegBasePath   ffmpegPathMap[platform];
    plugins.push(
      new CopyWebpackPlugin([
        {
          from: path.join(__dirname, ffmpegPath),
          to: path.join(__dirname, "core"),
          ignore: [".*"]
        }
      ])
    );
    config.plugins = [...config.plugins, ...plugins];
  }
};

第二步

在配置electron-builder打包过程,让ffmpeg放在指定位置

还是配置vue.config.js

代码语言:javascript复制
module.exports = {
  pluginOptions: {
    electronBuilder: {
      builderOptions: {
        appId: "cn.psvmc",
        productName: "星火智慧课堂",
        icon: "./app.ico",
        files: ["**/*", "static/*"],
        asar: true,
        win: {
          icon: "./app.ico",
          target: ["nsis"],
          extraResources: {
            from: "./core/",
            to: "./core/",
            filter: ["**/*"]
          }
        },
        nsis: {
          oneClick: false,
          allowElevation: true,
          allowToChangeInstallationDirectory: true,
          installerIcon: "./app.ico",
          uninstallerIcon: "./app.ico",
          installerHeaderIcon: "./app.ico",
          createDesktopShortcut: true,
          createStartMenuShortcut: true,
          license: "./LICENSE.txt"
        }
      }
    }
  }
}

主要就是添加了

代码语言:javascript复制
extraResources: {
    from: "./core/",
    to: "./core/",
    filter: ["**/*"]
}

第三步

调用时对应的路径

代码语言:javascript复制
import { resolve } from "path";
let ffmpegPath = "";
// 更新打包后 ffmpeg 路径
if (process.env.NODE_ENV === "production") {
    ffmpegPath = resolve(__dirname, "../core/ffmpeg");
} else {
    const { platform } = process;
    const ffmpegPathMap = {
        darwin: "darwin/x64/ffmpeg",
        win32: "win32/ia32/ffmpeg.exe",
        win64: "win32/x64/ffmpeg.exe",
        linux32: "linux/ia32/ffmpeg",
        linux64: "linux/x64/ffmpeg"
    };
    ffmpegPath = resolve(
        __dirname,
        "../../../../..",
        "ffmpeg-static/bin",
        ffmpegPathMap[platform]
    );
}

获取Windows音视频输入设备

代码语言:javascript复制
ffmpeg -list_devices true -f dshow -i dummy

0 人点赞