设置镜像 NPM镜像 1 2 npm config set registry https://registry.npm.taobao.org npm config list
还原默认
1 npm config set registry https://registry.npmjs.org
Electron镜像 查看配置文件的位置
可以查看到本机的userconfig在哪,即.npmrc文件在哪
比如我的
userconfig C:\Users\Jian.npmrc
打开该文件 添加
1 2 registry=https://registry.npm.taobao.org electron_mirror="https://npm.taobao.org/mirrors/electron/"
Vue CLi3环境配置 卸载旧版本
1 2 3 npm uninstall vue-cli -g yarn global remove vue-cli
安装新版本
1 2 3 npm install -g @vue/cli yarn global add @vue/cli
检查其版本是否正确 (3.x)
electron-builder 新项目添加依赖
安装插件
vue-cli-plugin-electron-builder
插件官网地址: https://nklayman.github.io/vue-cli-plugin-electron-builder/
选择Electron版本为5.0.0
Electron5.0和6.0的语法变化不大 选用5.0是因为node-ffi第三方修改版也只能支持到5.0
旧项目添加依赖 开发依赖添加
1 2 3 4 "devDependencies" : { "electron" : "5.0.0" , "vue-cli-plugin-electron-builder" : "^1.4.0" }
运行脚本添加
1 2 3 4 5 6 7 "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
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 83 84 85 86 87 88 89 "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" ;let win;protocol.registerSchemesAsPrivileged ([ { scheme : "app" , privileges : { secure : true , standard : true } } ]); function createWindow ( ) { win = new BrowserWindow ({ width : 800 , height : 600 , webPreferences : { nodeIntegration : true } }); if (process.env .WEBPACK_DEV_SERVER_URL ) { win.loadURL (process.env .WEBPACK_DEV_SERVER_URL ); if (!process.env .IS_TEST ) win.webContents .openDevTools (); } else { createProtocol ("app" ); win.loadURL ("app://./index.html" ); } win.on ("closed" , () => { win = null ; }); } app.on ("window-all-closed" , () => { if (process.platform !== "darwin" ) { app.quit (); } }); app.on ("activate" , () => { if (win === null ) { createWindow (); } }); app.on ("ready" , async () => { if (isDevelopment && !process.env .IS_TEST ) { try { await installVueDevtools (); } catch (e) { console .error ("Vue Devtools failed to install:" , e.toString ()); } } createWindow (); }); if (isDevelopment) { if (process.platform === "win32" ) { process.on ("message" , data => { if (data === "graceful-exit" ) { app.quit (); } }); } else { process.on ("SIGTERM" , () => { app.quit (); }); } }
就项目中的Electron中的静态页面建议放在public文件夹中
安装依赖报错
Error: Cannot find module ‘fs/promises’
NodeJS版本太低导致的
从NodeJS 14.0.0开始 暴露为 require('fs/promises')。
之前为require("fs").promises。
解决方法
升级NodeJS版本
步骤
使用14以上版本
1 2 3 nvm use 14.21.3 nvm list
常用配置 调试框分离 1 win.webContents.openDevTools({mode:'detach'});
隐藏菜单栏 1 2 import { Menu } from "electron" ;Menu .setApplicationMenu (null );
设置标题 窗口加载页面之前用的是窗口的title,加载之后用的是页面的title,所以最好两处都设置。
窗口的标题
1 2 3 4 5 6 7 8 9 10 11 12 const win = new BrowserWindow ({ width : 1200 , height : 600 , title : "码客脑图" , webPreferences : { webviewTag : true , webSecurity : false , enableRemoteModule : true , nodeIntegration : true , contextIsolation : false , }, });
页面的标题
1 2 3 4 5 6 7 <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width,initial-scale=1.0" > <link rel ="icon" href ="<%= BASE_URL %>favicon.ico" > <title > 码客脑图</title > </head >
运行报错 运行
报错
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
这是因为Devtools的安装需要翻墙
注释掉src/background.js中的以下代码就行了
1 2 3 4 5 6 7 8 if (isDevelopment && !process.env .IS_TEST ) { try { await installVueDevtools (); } catch (e) { console .error ("Vue Devtools failed to install:" , e.toString ()); } }
虽然还是会报错 但是不用等它尝试下载那么多次了 不用管这个错误即可
新版本需要注释的是
1 2 3 4 5 6 7 8 if (isDevelopment && !process.env .IS_TEST ) {try { await installExtension (VUEJS_DEVTOOLS ); } catch (e) { console .error ("Vue Devtools failed to install:" , e.toString ()); } }
打包配置 我们使用的vue-cli-plugin-electron-builder内部也是用electron-builder打包的,但是配置的位置不能像之前那样配置了
官方:https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/configuration.html
使用vue-cli-plugin-electron-builder的配置
在项目的根目录中的vue.config.js中添加以下配置
项目中没有发现vue.config.js这个文件,这是由于vue-cli3.0是号称0配置的,所以我们自己在根目录下创建一个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 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所在目录的相对位置,也就是说图标要放在项目的根目录,不是构建生成后目录的路径。app.ico 至少为 256x256
LICENSE.txt文件的编码必须为GBK编码
页面加载方式 之前直接用Electron写的代码,后来要结合Vue Cli3创建的项目,本来想的是直接把Electron的代码放在Vue的public目录中,加载的时候用下面的方式
1 2 3 4 5 6 7 8 9 10 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的规范,所以代码也要微改。
原写法
改为
加载页面方式改为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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
加载项目内文件 假如我们的项目下文件的位置为:/public/wordlist.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 readFileToArr (fReadName, callback ) { let fRead = fs.createReadStream (fReadName); let objReadline = readline.createInterface ({ input : fRead }); let arr = []; objReadline.on ("line" , function (line ) { arr.push (line); }); objReadline.on ("close" , function ( ) { callback (arr); }); } readFileToArr ("public/wordlist.txt" ,function (arr ) {})
这样在开发时我们能访问到,但是打包后它会从程序的根目录中查找/public/wordlist.txt,但是文件的位置并不在此处,我们就要设置文件的位置
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 module .exports = { pluginOptions : { electronBuilder : { builderOptions : { appId : "cn.psvmc.cidian" , productName : "词典" , icon : "./app.ico" , extraResources : { from : "./public/wordlist.txt" , to : "../public/wordlist.txt" }, 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" } } } } };
其中主要配置
1 2 3 4 extraResources: { from: "./public/wordlist.txt" , to: "../public/wordlist.txt" }
小贴士
如果是electron-vue ,在vue中使用process.cwd()方法可以读取到打包后的根目录path,不过默认也是从项目根目录算起。
如
1 2 3 4 5 6 7 8 let file = process.cwd () + 'config.json' ;fs.readFile (file, 'utf-8' , function (err, data ) { if (err) { console .log (err) } else { console .log (data) } })
ffmpeg 调用二进制文件(FFMPEG) Electron默认是开启asar的,导致二进制文件也被打在了asar文件中,就不能再被调用,所以我们要让二进制文件不被处理。
首先我们要知道程序的打包步骤
webpack打包 => electron-builder打包(asar打包 => exe打包)
考虑到不同平台需要打入的ffmpeg不同,我们可以在webpack打包过程中筛选使用的文件,过程如下:
如果软件不考虑多平台,那么可以直接下载对应平台的ffmpeg放在项目根目录下的core文件夹中,下面的第一步可以跳过不用配置。
第一步
安装ffmpeg-static和copy-webpack-plugin
1 2 npm install --save-dev ffmpeg-static npm install --save-dev copy-webpack-plugin
配置vue.config.js
把对应平台的文件放在项目根目录的core文件夹中
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 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
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 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" } } } } }
主要就是添加了
1 2 3 4 5 extraResources: { from: "./core/" , to: "./core/" , filter: ["**/*" ] }
第三步
调用时对应的路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import { resolve } from "path" ;let ffmpegPath = "" ;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音视频输入设备 1 ffmpeg -list_devices true -f dshow -i dummy