从0到1搭建electron+vite+vue3模板
实现目标
- 能正常启动,打包
- 自己搭建打包过程,使用node
实现方案
- 网上常见的打包方案
- 修改main.js,在里面加载vite的dist包
- package,启动命令行先使用vite的打包,再使用electron的启动
- gitee-SKY-Electron-Vite-Template 本文使用的方案
- 打包思路 使用node配合vite和electron进行打包
自己创建一个模板
思路:使用vite编译vue软件打包,再使用electron引用打包后的文件
学习:
通过vite启动一个web版
通过rollup编译electron
启动electron,连接vite
1. 创建模板并能运行网页版
1. 使用npm init命令创建一个模板
-
name,version, description, main为必填项
-
main需要设置为main.js(也可不要,下面代码注意)
2. 引入基础版所需要的依赖
// electron 用于启动和编译应用程序包
npm install electron --save-dev
// vite 用于启动和编译vue程序包
npm install vite --save-dev
// vue vue语言包
npm install vue --save-dev
// @vitejs/plugin-vue 用于编译vue文件
npm install @vitejs/plugin-vue --save-dev
// 用于获取端口使用情况
npm install portfinder --save-dev
3. 创建需要的文件夹和文件
-
创建.electron-vite文件夹用于放置启动和编译
- dev-runner.js 用于启动
//* 设置环境变量 process.env.NODE_ENV = 'development' //* 依赖引入 const path = require('path') // node内置 用来获取路径 const { createServer } = require('vite') //* createServer 用于启动vue项目 const Portfinder = require("portfinder") //* 用于获取端口 //* 配置项引入 const viteConfig = require('./vite.config') //* 引入vite创建需要的配置项 const envConfig = require('../config') //* 引入环境变量需要的配置项 function startWeb() { //* 启动vite项目 //* 通过环境变量获取端口 通过PortFinder判断端口。通过vite方法进行启动和监听端口 return new Promise((resolve, reject) => { Portfinder.basePort = envConfig.dev.port || 9080 //* 配置要检查的端口 Portfinder.getPort(async (err, port) => { //* 检查端口是否能正常使用 if(err) {//* 有错误则返回出去 reject('PortError: ' + err) return } //* 如果端口没问题,则通过vite进行创建 const server = await createServer(viteConfig) process.env.PORT = port //* 设置环境变量 //* 监听该端口 进行启动 server.listen(port).then(() => { console.log('port:', port) }) resolve() }) }) } //* 启动方法 function start() { startWeb() } //* 启动 start()- vite.config.js vite启动配置
//* 引入依赖 const { resolve } = require('path') const { defineConfig } = require('vite') //* 用于解析config配置 const vue = require("@vitejs/plugin-vue") //* vue支持 const root = resolve('src/renderer') //* 入口文件 index.html所在的位置 //* 生成配置 const config = defineConfig({ root,//* 入口文件 index.html所在的位置 mode: process.env.NODE_ENV, //* 默认'development'(serve),'production'(build)配置环境变量 define: { //* 设置全局变量 'process.env': process.env.NODE_ENV === 'production' ? userConfig.build.env : userConfig.dev.env }, build: { //* 打包配置 outDir: resolve('dist/electron/renderer'), //* 打包文件夹 emptyOutDir: true, //* 打包时清空目录 }, plugins: [vue()], publicDir: resolve('static') //* 配置静态文件夹,并且会打包到outDir下 }) module.exports = config -
创建static文件夹用于放置资源文件
-
创建src文件夹用于放置代码
-
创建renderer文件夹来放置vue代码
- 创建index.html文件来作为vite启动页面
<html lang="en"> <body> <div id="app"></div> <script type="module" src="/main.js"></script> </body> </html>-
创建main.js作为入口文件 引入App.vue进行显示
import { createApp } from 'vue'; import App from './App.vue' const app = createApp(App) app.mount('#app') -
创建App.vue来作为app显示内容
<template> <div class="app-container">Hello World!!!</div> </template> -
创建main文件夹来放置electron的方法
-
-
创建config文件夹用于放置环境变量
-
dev.env.js:dev 开发环境-变量
module.exports = { NODE_ENV: 'development' } -
prod.env.js 正式环境-变量
module.exports = { NODE_ENV: 'production' } -
index.js 通用-变量
module.exports = { build: { env: require("./prod.env"), // 配置环境变量 }, dev: { env: require("./dev.env"), port: 9080,//* 开发环境下使用的端口 }, };
-
4. 启动网页版的vite
上面代码最主要的是src/renderer的vue代码和.electron-vite文件夹内代码的实现。通过vite-config.js提供的配置,使用vite的createServer进行网页的启动。下面进行最后的配置:
- package.json启动命令配置
该命令将使用咱们上面配置的.electron-vite/dev-runner.js进行启动
"scripts": {
"start": "node .electron-vite/dev-runner.js"
},
- 命令行进行启动
npm start
如果成功的话根据终端打印的port可以在网页上面进行查看
2. 配置编译electron
需要使用rollup对文件进行编译
-
ollup-plugin-node-resolve: 告诉 Rollup 如何查找外部模块
-
rollup-plugin-commonjs:将CommonJS模块转换为 ES2015 供 Rollup 处理,请注意,rollup-plugin-commonjs应该用在其他插件转换你的模块之前 - 这是为了防止其他插件的改变破坏CommonJS的检测
1. 依赖引入
//* 引入rollup
npm install @rollup/plugin-commonjs --save-dev
2. 使用到的文件和文件夹
-
.electron-vite 添加rollup.config.js
-
rollup.config.js 用于electron的编译配置项
//* 依赖引入 const path = require('path') const replace = require("@rollup/plugin-replace"); //* 变量替换插件 const alias = require('@rollup/plugin-alias') const { builtinModules } = require('module') //* 不想打包的模块名 const config = (env = 'production') => { const mainConfig = { input: path.join(__dirname, '../src/main/index.js'), output: { //* 输出 file: path.join(__dirname, '../dist/electron/main/main.js'), format: 'cjs', name: 'MainProcess', sourcemap: false, }, plugins: [ replace({ //* 替换路径 preventAssignment: true, "process.env.NODE_ENV": JSON.stringify(env), }), alias({ //* 路径别名 entries: [ { find: '@main', replacement: path.join(__dirname, '..', 'src', 'main'), }, { find: '@config', replacement: path.join(__dirname, '..', 'config') }, { find: '@common', replacement: path.join(__dirname, '..', 'src', 'common') }, ] }) ], external: [ //* 需要在项目中使用的依赖需要在这里引用 ...builtinModules, 'electron' ], } return mainConfig } module.exports = config -
dev-runner.js 添加编译方法
const rollup = require('rollup')//* 构建包的工具 const rollupConfig = require('./rollup.config') const rollupOption = rollupConfig(process.env.NODE_ENV) function startMain() { //* 构建electron文件 return new Promise((resolve, reject) => { //* 创建一个监听模块处理的对象 const watcher = rollup.watch(rollupOption) //* 监听构建事件 会有code https://www.rollupjs.com/guide/javascript-api watcher.on('event', event => { //* code等于END代表完成所有文件束构建 if(event.code === 'END') { //* 等构建完成后发送出去 通知可以打开 resolve() } else if (event.code === 'ERROR') { reject(event.error) } }) }) } //* 启动方法 async function start() { await startWeb() //* 启动网页版 await startMain() //* 构建包 }
-
-
src/main
-
index.js electron应用程序的入口
//* 依赖引入 import { app } from 'electron' import InitWindow from './services/windowManager' //* 监听启动完成事件 则打开窗口 function onAppReady() { //* App准备完成事件 new InitWindow().initWindow() } app.whenReady().then(onAppReady) app.on('window-all-closed', () => { // 所有平台均为所有窗口关闭就退出软件 app.quit() })
-
-
src/main/services
-
windowManager 用来管理窗口 创建窗口等
//* 依赖引入 import { BrowserWindow } from 'electron' import { join } from 'path' //* 路径 import { getUrl } from '../utils/URL' const winURL = getUrl("", join(__dirname, '..', 'renderer', 'index.html')); //* 获取vue的路径 // const winURL = 'http://localhost:9080/' class MainInit { mainWindow = null createMainWindow() { //* 创建一个窗口对象 this.mainWindow = new BrowserWindow({ titleBarStyle: 'default', //* 设置标题栏样式 }) //* 加载vue页面 this.mainWindow.loadURL(winURL) this.mainWindow.openDevTools(); } initWindow() { //* 初始化窗口 console.log('<===== 要来创建窗口') this.createMainWindow() } } export default MainInit
-
-
src/main/utils
- URL.js 工具类 getUrl方法用于获取连接vue项目的链接
const isDev = process.env.NODE_ENV === 'development'; /** * 获取真正的地址 * * @param {string} devPath 开发环境路径 * @param {string} proPath 生产环境路径 * @param {string} [hash=""] hash值 * @param {string} [search=""] search值 * @return {*} {string} 地址 */ export function getUrl(devPath, proPath, hash = "", search = "") { const url = isDev ? new URL(`http://localhost:${process.env.PORT}`) : new URL('file://'); url.pathname = isDev ? devPath : proPath; url.hash = hash; url.search = search; return url.href; }
3. 编译应用程序
为什么要编译应用程序?
请先编译,在启动electron会说一下大概流程
- 使用npm start命令进行启动
- 查看dist/electron/main/main.js文件是否存在,存在即为成功
3. 启动electron
回顾:
刚才的教程中第一步为启动vite项目,让其可以在网页中浏览。第二步为编译electron应用程序包。
electron简单来说只是一个壳,引用了js项目进行渲染显示。那么启动逻辑就很简单了。
- 启动编译后的electron程序
- electron程序会通过loadURL获取vite项目进行渲染。这就是为什么我们需要启动vite项目让其可以在网页上面访问的原因。比如loadURL('http://localhost:8090')。
1. 使用到的文件和文件夹
-
.electron-vite
-
dev-runner.js
const { spawn } = require('child_process') //* 创建异步线程 用于创建electron const electron = require('electron') function startElectron() { //* 构建参数 var args = [ '--inspect=5858', path.join(__dirname, '../dist/electron/main/main.js') ] //* 添加环境变量参数 为node地址和当前文件地址 // detect yarn or npm and process commandline args accordingly if (process.env.npm_execpath.endsWith('yarn.js')) { args = args.concat(process.argv.slice(3)) } else if (process.env.npm_execpath.endsWith('npm-cli.js')) { args = args.concat(process.argv.slice(2)) } //* 异步添加子线程 参数1:要执行的命令 参数2:字符串参数列表 electronProcess = spawn(electron, args) electronProcess.on('close', () => { process.exit() }) } //* 启动方法 async function start() { await startWeb() //* 启动网页版 await startMain() //* 构建包 await startElectron()//* 创建electron应用 }
-
2. 启动
npm start
至此,start环节结束
编译
1. 安装依赖
//* 用于删除文件 构建之前需要先清空文件
npm install del --save-dev
//* electron打包工具
npm install electron-builder --save-dev
//* 热更新 忽略
electron-updater
2. 修改package配置
配置打包的index.js位置。并且通过该文件引用的依赖去打包需要的文件
{
"main": "./dist/electron/main/main.js",
"scripts": {
"build": "node .electron-vite/build.js && electron-builder -c build.json"
}
}
3. 用到的文件和文件夹
思路:electron只是一个打包工具,一个壳。所以总思路还是壳引用vue页面
- 使用vite打包web包到dist文件夹中
- 使用rollup打包electron启动需要的main.js到dist文件夹
- 使用electron-builder打包dist文件夹中的东西到app包中
-
.electron-vite
-
vite.config.js
const config = defineConfig({ base: './', //* 打包之后的index.html里面的引用地址 因为打包之后是地址引用file://。如果使用/的话会导致查找不到文件夹的问题 }) -
build.js
//* 引入依赖 const { sync } = require('del') //* 删除文件 构建之前需要先删除文件 const rollup = require('rollup')//* 构建electron的工具 const { build } = require('vite') //* 构建web包的工具 //* 引入配置 //* 配置项引入 const viteConfig = require('./vite.config') //* 引入vite创建需要的配置项 const rollupConfig = require('./rollup.config') const rollupOption = rollupConfig(process.env.NODE_ENV) //* 获取electron打包配置 function buildApp() { //* 构建App包 return new Promise((resolve, reject) => { //* 删除使用的包 sync(['dist/electron/main/*']) rollup.rollup(rollupOption) .then(build => { build.write(rollupOption.output) console.log('App Build Success!!!') resolve() }) .catch(error => { console.log('App Build Fail!!!', error) reject(error) }) }) } function buildWeb() { //* 构建web包 return new Promise((resolve, reject) => { //* 删除包 sync(['dist/electron/renderer/*']) build(viteConfig).then((res) => { console.log('Web Build Success!!!') resolve() }) .catch(() => { console.log('Web Build Faild!!!') reject() }) }) } function start() { //* 启动构建方法 Promise.all([buildApp(),buildWeb()]) .then(()=> { //* 成功退出 process.exit() }) .catch(() => { //* 报错 退出环境 code:1 为错误 process.exit(1) }) } start()
-
-
build.json
{ "asar": false,//* 是否使用asar打包 "appId": "org.electron-demo",//* 软件id "productName": "electron-vite-demo",//* 打包出来的软件名 "directories": { //* 输出路径 打包后路径 "output": "build" }, "mac": { //* icon路径 "icon": "build/icons/icon.icns" }, "win": { "icon": "build/icons/icon.ico", "target": "nsis" }, "linux": { "target": "deb", "icon": "build/icons" } } -
build文件夹,可根据build.json中的icon手动添加icon。并且为编译后的文件存放地址
4. 启动
npm run build
如果正常编译 不报错即为成功
5. 编译后文件夹用途
build/xxx.exe 为安装文件 会安装到电脑进行使用
build/win-unpacked文件夹为免安装版 可以直接使用
链接
- 多彩文字
- 配置项
- devTools配置
- 版本自动更新
- preload.js使用