4、前端框架集成:在Electron中集成React/Vue;使用Vite作为构建工具;配置热更新(HMR);解决跨域与资源加载问题

好,咱们进入第四章节。说实话,这一章是很多初学者觉得「头大」的地方。为什么?因为Electron本身是个壳,真正干活的是前端框架。怎么把React或Vue塞进去,怎么让开发体验像写Web应用一样爽,怎么处理那些烦人的跨域问题——嗯,今天咱们一次性说清楚。

4.1 为什么选Vite?

我个人习惯用Vite作为构建工具。原因很简单:快。真的快。

以前用Webpack,改一行代码等两三秒刷新,那种感觉就像在等老电脑开机。Vite基于ES Module,开发时按需编译,冷启动几乎秒开。我在项目中遇到过一个大项目,Webpack启动要40秒,换成Vite后直接降到2秒。你想想看,这体验差距有多大。

当然,Vite对Electron的支持也很成熟。社区有现成的插件,比如electron-vite,或者直接用vite-plugin-electron。我个人更推荐后者,配置灵活,而且不侵入原有Vite配置。

4.2 集成React

先说说React。如果你用Vite创建React项目,默认模板就带HMR。但集成到Electron里,有几个关键点要注意。

首先,安装依赖:

npm create vite@latest my-electron-app -- --template react
cd my-electron-app
npm install electron vite-plugin-electron electron-builder --save-dev

然后,在vite.config.js里配置插件:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import electron from 'vite-plugin-electron'

export default defineConfig({
  plugins: [
    react(),
    electron([
      {
        entry: 'electron/main.js',
      },
      {
        entry: 'electron/preload.js',
        onstart(args) {
          args.reload()
        },
      },
    ]),
  ],
})

这里有个坑。我曾经在配置onstart时忘了写args.reload(),结果每次改主进程代码都要手动重启。嗯,这个小细节浪费了我半小时。

React组件里的热更新是自动的。你改个JSX,页面瞬间刷新。但要注意,如果改了主进程代码,Electron窗口会整个重启,这是正常的。

4.3 集成Vue

Vue的集成思路和React差不多。Vite官方对Vue的支持更好,毕竟都是尤大出品。

创建项目:

npm create vite@latest my-vue-electron -- --template vue
cd my-vue-electron
npm install electron vite-plugin-electron electron-builder --save-dev

配置vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import electron from 'vite-plugin-electron'

export default defineConfig({
  plugins: [
    vue(),
    electron([
      {
        entry: 'electron/main.js',
      },
    ]),
  ],
})

Vue的HMR体验比React更丝滑。因为Vue的模板编译是运行时进行的,改模板几乎无感知。我在做一个后台管理系统时,Vue的HMR从来没让我失望过。React偶尔会因为状态丢失需要手动刷新,Vue基本不用操心。

4.4 配置热更新(HMR)

HMR说白了就是「改代码不刷新页面,只替换改动的模块」。这玩意儿在Electron里稍微有点特殊。

为什么?因为Electron有两个进程:主进程和渲染进程。渲染进程的HMR和Web开发一样,但主进程的HMR需要特殊处理。

我建议的做法是:

  • 渲染进程:直接用Vite自带的HMR,不用额外配置
  • 主进程:用vite-plugin-electrononstart回调,检测到变化后重启Electron
  • 预加载脚本:同样用onstart,但调用args.reload()只刷新窗口

这里有个小技巧。如果你觉得每次改主进程都重启窗口太慢,可以试试electron-reloader这个包。它能在不重启窗口的情况下热更新主进程代码。不过说实话,稳定性一般,我试过几次后还是回到了重启方案。

我的建议:开发时把主进程代码写得尽量少,逻辑都放在渲染进程。这样你大部分时间都在享受渲染进程的秒级HMR,主进程的重启频率会很低。

4.5 解决跨域与资源加载问题

跨域问题,是Electron开发里最容易被忽视的坑。你想想看,Electron的渲染进程加载的是file://协议,而你的API请求是http://协议。浏览器会直接拦截。

解决方案有三种:

方案 适用场景 优缺点
设置webSecurity: false 开发环境快速调试 简单但不安全,生产环境不能用
使用contextBridge + 主进程代理 生产环境推荐 安全,但需要额外写代理代码
配置CORS头(后端配合) 后端可控制时 最规范,但需要后端改代码

我个人最常用的是第二种。在预加载脚本里暴露一个API,主进程用net模块发请求,这样既安全又灵活。

举个例子:

// preload.js
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('api', {
  fetch: (url, options) => ipcRenderer.invoke('fetch', url, options)
})

// main.js
const { ipcMain, net } = require('electron')

ipcMain.handle('fetch', async (event, url, options) => {
  const response = await net.fetch(url, options)
  return response.json()
})

这样渲染进程里直接调用window.api.fetch('/api/data'),完全不会有跨域问题。

注意:千万不要在生产环境关闭webSecurity。我曾经见过一个项目,开发时图省事关了,上线后被人用XSS攻击,用户数据泄露。这个教训很深刻。

4.6 资源加载问题

资源加载,说白了就是图片、字体、静态文件怎么在Electron里正确显示。

在Web开发里,你写<img src="/logo.png">,浏览器会自动从服务器根目录加载。但在Electron里,/指向的是file://协议的根目录,也就是你的应用安装目录。这会导致路径错乱。

我的做法是:

  • 开发时:把静态资源放在public文件夹,Vite会自动处理路径
  • 生产时:用__dirnameapp.getAppPath()拼接绝对路径
  • 最佳实践:所有资源都通过Vite的import引入,让构建工具处理路径

举个例子:

// 不推荐
const img = new Image()
img.src = 'assets/logo.png'  // 路径容易出错

// 推荐
import logo from './assets/logo.png'
const img = new Image()
img.src = logo  // Vite会处理成正确路径

嗯,这里要注意。如果你用了file://协议加载本地文件,记得在BrowserWindowwebPreferences里设置webSecurity: false(仅开发环境)。或者更安全的方式,用protocol.registerFileProtocol注册自定义协议。

核心总结:

  • Vite是Electron开发的最佳拍档,快就一个字
  • React和Vue的集成方式几乎一样,选你喜欢的就行
  • HMR默认支持渲染进程,主进程需要额外配置
  • 跨域问题用主进程代理解决,别偷懒关安全设置
  • 资源加载用Vite的import机制,别手动拼路径

好了,这一章的内容就这些。下一章咱们聊聊打包和分发,那才是真正让应用「落地」的关键一步。到时候见。