先说结论,重点还是在于mainWindow.loadURL()
。
打包后还是加载
http://localhost:3000
是无法运行的,因此,此处需要先用vite打包好,然后使用electron-builder
加载vite打包后的文件进行打包。为了代码能够根据不同环境在运行时加载
http://localhost:3000
,在打包时加载文件,此处需要使用环境变量来切换生产和开发环境。
vite.config.ts 配置
渲染进程配置 renderer/vite.config.ts
本文基于开启 nodeIntegration
,如果无需在渲染进程中使用 Node.js API 可以忽略渲染进程配置
渲染进程代码 main/index.ts:
即 npm create vite
生成代码
主进程配置 main/vite.config.ts
Vite 提供了 build.lib
快捷入口,使得主进程配置十分简单;其次只需要关注下 Rollup 的 external 即可
import { builtinModules } = from 'module' export default { root: __dirname, // 指向主进程目录 build: { outDir: '../../dist/main', lib: { entry: 'index.ts', // Electron 目前只支持 CommonJs 格式 formats: ['cjs'], fileName: () => '[name].cjs', }, rollupOptions: { external: [ // 告诉 Rollup 不要打包内建 API 'electron', ...builtinModules, ], }, }, } 复制代码
主进程代码 main/index.ts:
import { app, BrowserWindow } from 'electron' app.whenReady().then(() => { win = new BrowserWindow({ title: 'Main window', webPreferences: { preload: join(__dirname, '../preload/index.cjs'), nodeIntegration: true, contextIsolation: false, }, }) if (app.isPackaged) { win.loadFile(join(__dirname, '../renderer/index.html')) } else { win.loadURL('http://localhost:3000') } }) 复制代码
启动脚本分析
先出个结论 - Electron 的启动与 Node.js 相比行为几乎是一致的 - 可执行程序
+ 入口文件
# 当我们使用 Node.js 执行一个文件 node path/filename.js # 这里使用的是全局的 node 命令 # 当我们使用 Electron 执行一个文件 node_modules/.bin/electron path/filename.js # 这里使用的是项目中的 electron 复制代码
再思考下关于 Electron 启动的问题
- 开发环境下 Electron 加载 Vite 启动的开发服务器,即通过
loadURL()
加载一个 http 地址 - 生产环境下使用
loadFile()
加载一个入口文件
启动脚本设计
Vite 提供了全量的可编程化的 Node.js API 方便我们灵活调度,比如 vite server
命令对应 API 中的 createServer().listen()
。
基于它我们可以很方便的在我们的脚本中启动 Vite
scripts/watch.mjs
import { spawn } from 'child_process' import { createServer, build } from 'vite' // electron 绝对路径,Windows 为 electron.exe import electronPath from 'electron' // ---- 渲染进程部分 ---- const server = await createServer({ configFile: 'packages/renderer/vite.config.ts' }) await server.listen() // ---- 主进程部分 ---- let electronProcess = null build({ // 加载主进程构建配置 configFile: 'packages/main/vite.config.ts', build: { // 通过 watch 选项监听主进程文件改动,时时编译 watch: {}, }, plugins: [{ name: 'electron-main-starter', // 第一次编译、重新编译后都会触发 writeBundle() { if (electronProcess) { // 重启前先杀死当前正在运行的 electron 程序 electronProcess.kill() } // 启动、重启 electron electronProcess = spawn( // 相当于官方的 electron . 启动方式 electronPath, ['.'], // 将 electron 主进程的 console.log 输出到当前命令行 { stdio: 'inherit' }, ) }, }], }) 复制代码
配置
{ "scripts": { "dev": "node scripts/watch.mjs" } } 复制代码
命令
npm run dev 复制代码
到这一步已经完成了,已经是一个可用的应用了!
渲染进程使用 Node.js API
renderer/src/main.ts
默认情况下 Vite 会将所有的 import
裸模块以 ESM 格式预构建到 node_modules/.vite/
文件夹下
- Node.js 内置模块会被当成 external 模块处理,但只是一个简单的 polyfill 并不能在渲染进程中使用
- electron 模块会执行预构建,得到的结果也是不可以使用的,因为 electron 导出的只是一个程序运行路径而已
上述两点,需要我们对其有正确的处理才能保障其在渲染进程中工作
- 第一种方式,如果使用
require('electron')
可以避开预构建和 polyfill 行为,从而正常工作 - 如果将
import electron form 'electron'
在与构建中排除,并在 Vite 对其 polyfill 之前进行拦截处理,也可以使其正常工作
// ❌ 不会正常工作 import { ipcRenderer } from 'electron' // ✅ 可以正常工作 const { ipcRenderer } = require('electron') 复制代码
到此为止,使用部分已经 OK 了!除了看起来丑陋的
require('electron')
总结
- Vite 个人觉得是个不错的方案,毕竟打包工具早晚会推出历史舞台;Vite 往前又迈了 0.5步
- Electron 的集成只是一个案例,从一个案例出发到写一个插件,你会更好的理解 Vite 设计、思想
- 最后,不能什么都站在客观的角度去等待,更需要我们主动的去建设