electron:vue3+vite打包案例

1、安装electron

首先设置镜像源,否则安装会非常非常慢。

打开npm的配置文件。

npm config edit

修改配置项。

registry=https://registry.npmmirror.com

electron_mirror=https://cdn.npmmirror.com/binaries/electron/

electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

镜像源设置好后,就可以npm install electron了。

2、安装concurrently

concurrently是本地运行electron需要用到的插件,npm install concurrently即可。

3、electron的main.js和preload.js文件

在项目根目录下,创建electron文件夹,在该文件夹下创建main.js和preload.js。

main.js代码如下:

javascript 复制代码
const {
	app,
	BrowserWindow
} = require('electron')
const path = require('node:path')

const createWindow = () => {
	const win = new BrowserWindow({
		width: 800,
		height: 600,
		webPreferences: {
			preload: path.join(__dirname, 'preload.js')
		}
	})
	// 连接本机前端页面的端口号
	win.loadURL("http://localhost:3000")
	win.webContents.openDevTools()
}

app.whenReady().then(() => {
	createWindow()
})

// 关闭所有窗口时,退出
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

preload.js代码如下:

由于本案例只是一个demo,功能简单,preload里并无功能性代码,只是为了保证代码项目结构完整性,对应了main.js里的preload: path.join(__dirname, 'preload.js')

javascript 复制代码
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,我们也可以暴露变量
})

在package.json里设置main的路径,也就是我们在根目录下创建的electron文件下的main.js。script里添加启动命令命令。

javascript 复制代码
"main": "electron/main.js",
"scripts": {
		"dev": "concurrently \"vite\" \"electron .\""
	}

运行npm run dev,concurrently 会同时启动vite和electron,只启动electron的话,页面会是一片空白。

本地运行结果如下,打开了调试工具:

4、安装electron-builder

electron-builder是用来将项目打包成exe文件的。

之前已经设置了镜像源,此时只需要npm install electron-builder即可。

打包一共需要两个步骤,先用vite打包成dist,然后用electron-builder打包成exe。

所以在package.json的script里,我们将这两个步骤合并一下:

javascript 复制代码
"scripts": {
		"dev": "concurrently \"vite\" \"electron .\"",
		"build": "vite build && electron-builder"
	},

还需要进行其他打包设置,比如设置icon路径,icon也放在根目录下的electron文件夹里,target设置为nsis,会生成安装程序的exe;files是打包后需要包含的文件,dist是vite build生成的文件夹,electron文件夹下有main.js和preload.js,package.json里包含了重要的配置信息,所以把它们都放在打包里;directories的output是electron-builder打包后的文件夹名称,设置为release,跟dist文件夹同级。

javascript 复制代码
"build": {
		"appId": "electron_multiple_languages",
		"win": {
			"icon": "electron/icon.ico",
			"target": "nsis"
		},
		"files": [
			"dist/**/*",
			"electron/**/*",
			"package.json"
		],
		"directories": {
			"output": "release"
		}
	},

打包后不需要显示调试工具了,main.js里需要判断生产环境还是开发环境:

如果是生产环境,需要从dist文件夹的index.html入口,如果是开发环境,连接本地,打开调试工具。

注意端口号不一定是3000,根据你项目实际的端口号来写。

javascript 复制代码
if(app.isPackaged){
		win.loadFile(path.join(__dirname, '../dist/index.html'))
	}else{
		// 连接本机前端页面的端口号
		win.loadURL("http://localhost:3000")
		win.webContents.openDevTools()
	}

执行npm run build命令。

首次使用electron-builder打包时,会到github上去下载winCodeSign和nsis的二进制文件,由于网络问题经常失败。

可以直接去镜像源地址里下载它们:

winCodeSign-2.6.0

nsis需要下载两个包:

CNPM Binaries Mirror

下载好,解压,放在cache目录下:

打包过程中还会遇到其他问题,比如:

icon的分辨率不合规,必须是256*256;

package.json里缺少author和description;

electron和electron-builder必须在devDependencies里。

具体问题按照错误提示来修改即可。

打包后的目录结构:

5、删除dist和release

每次生成的dist文件夹里文件名不相同,如果想要反复打包,dist里的文件是无法自动覆盖的,最好每次都删除dist和release文件夹,需要借助插件rimraf。

npm install rimraf安装,增加删除命令:

javascript 复制代码
"scripts": {
		"clean": "rimraf dist release",
		"dev": "concurrently \"vite\" \"electron .\"",
		"build": "npm run clean && vite build && electron-builder"
	},

7、白屏问题

双击release文件夹下的setup.exe文件,安装程序,会在桌面创建快捷方式,双击启动程序,但是会发现一片空白。

这个问题卡了好几个小时,一遍一遍问deepseek,终于找到了问题所在,打包后文件加载要用相对路径,需要在vite.config.js里设置base:'./'

注意,在vite.config.js里是设置base,如果是vue.config.js,是设置publicPath,根据自己项目实际情况来写。

最终运行结果:

完整代码:

electron文件夹下的main.js

javascript 复制代码
const {
	app,
	BrowserWindow
} = require('electron')
const path = require('node:path')

const createWindow = () => {
	const win = new BrowserWindow({
		width: 800,
		height: 600,
		webPreferences: {
			preload: path.join(__dirname, 'preload.js')
		}
	})
	if(app.isPackaged){
		win.loadFile(path.join(__dirname, '../dist/index.html'))
	}else{
		// 连接本机前端页面的端口号
		win.loadURL("http://localhost:3000")
		win.webContents.openDevTools()
	}
}

app.whenReady().then(() => {
	createWindow()
})

// 关闭所有窗口时,退出
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

electron文件夹下的preload.js

javascript 复制代码
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,我们也可以暴露变量
})

package.json

javascript 复制代码
{
	"name": "electron_multiple_languages",
	"version": "0.0.0",
	"author": "demo@163.com",
	"main": "electron/main.js",
	"scripts": {
		"clean": "rimraf dist release",
		"dev": "concurrently \"vite\" \"electron .\"",
		"build": "npm run clean && vite build && electron-builder"
	},
	"build": {
		"appId": "electron_multiple_languages",
		"win": {
			"icon": "electron/icon.ico",
			"target": "nsis"
		},
		"files": [
			"dist/**/*",
			"electron/**/*",
			"package.json"
		],
		"directories": {
			"output": "release"
		}
	},
	"dependencies": {
		"concurrently": "^9.2.0",
		"rimraf": "^6.0.1",
		"vue": "^3.2.8"
	},
	"devDependencies": {
		"@vitejs/plugin-vue": "^1.6.0",
		"@vue/compiler-sfc": "^3.2.6",
		"electron": "^37.2.5",
		"electron-builder": "^26.0.12",
		"vite": "^2.5.2"
	}
}

vite.config.js

javascript 复制代码
import {
	defineConfig
} from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
	plugins: [vue()],
	base: './', // 确保资源使用相对路径
})