
本文详解 Electron-Vite 项目中多 HTML 入口(如 index.html 和 video.html)在生产环境白屏或报错“Not allowed to load local resource”问题的根因与完整解决方案,涵盖 Vite 构建配置、路径解析、安全策略适配及 IPC 通信修复要点。
本文详解 electron-vite 项目中多 html 入口(如 `index.html` 和 `video.html`)在生产环境白屏或报错“not allowed to load local resource”问题的根因与完整解决方案,涵盖 vite 构建配置、路径解析、安全策略适配及 ipc 通信修复要点。
在 Electron-Vite + React 项目中,开发者常误以为只要 index.html 能正常加载,其他 HTML 文件(如 video.html)也能通过 loadFile() 直接打开——但实际在生产构建后,Vite 默认仅将 src/renderer/index.html 作为唯一入口进行打包和资源注入,导致 video.html 及其关联的 JS/CSS 资源未被构建、未被复制到输出目录(如 out/renderer/),最终 loadFile() 尝试加载一个“不存在的文件”,触发 Chromium 的 Not allowed to load local resource 错误。
✅ 正确做法:显式声明多 HTML 入口
Vite 的 build.rollupOptions.input 支持对象语法,可为每个 HTML 页面指定独立入口。需在 electron.vite.config.js 中明确注册 video.html:
// electron.vite.config.js
import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()]
},
preload: {
plugins: [externalizeDepsPlugin()]
},
renderer: {
build: {
rollupOptions: {
// 关键配置:声明多个 HTML 入口
input: {
index: resolve(__dirname, 'src/renderer/index.html'),
video: resolve(__dirname, 'src/renderer/video.html')
}
}
},
resolve: {
alias: {
'@renderer': resolve('src/renderer/src')
}
},
plugins: [react()]
}
})? 为什么必须这样?
Electron-Vite 的renderer构建阶段本质是运行一次 Vite 构建。默认input: 'src/renderer/index.html'意味着只有该文件及其<script type="module"></script>引用链会被处理、转译、哈希重命名并输出到out/renderer/。而video.html若未声明,则不会生成对应产物(如out/renderer/video-CxYz1234.html),loadFile()自然失败。
✅ 构建后路径验证与加载逻辑优化
构建完成后,检查 out/renderer/ 目录是否生成了两个 HTML 文件(如 index.html 和 video-xxxxx.html)。此时 loadFile() 必须指向构建后的绝对路径,而非开发时的源路径:
// main/index.js(生产环境加载逻辑)
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
// 开发:走 dev server(http://localhost:5173/video.html)
secondWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/video.html`)
} else {
// 生产:必须使用 join(__dirname, '../renderer/video.html') ——
// 注意:此处的 '../renderer/video.html' 是构建后相对路径,
// 实际由 Vite 输出到 out/renderer/ 下,electron-builder 会自动拷贝
secondWindow.loadFile(join(__dirname, '../renderer/video.html'))
}⚠️ 重要提醒:loadFile() 的路径是相对于 app.getAppPath()(即主进程所在目录),而 electron-builder 打包时会将 out/renderer/ 内容复制到 dist/ 或 resources/app.asar/renderer/。因此确保 electron-builder 配置中 files 字段包含 renderer/**/*(默认已包含)。
立即学习“前端免费学习笔记(深入)”;
? 安全策略兼容性检查
你的 webPreferences 启用了 webSecurity: true(默认值),这是推荐的安全实践。但需注意:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
-
loadFile()加载本地 HTML 时,Chromium 会启用严格的同源策略(file://协议),禁止跨目录请求资源(如fetch('../public/data.json')); - 若
video.html中需加载本地 JSON、调用 Node API 或读取文件,必须通过preload脚本桥接,严禁在渲染进程中直接使用fs或require; - 确保
preload/index.js中正确暴露所需能力,并在video.html的<script></script>中通过window.electron.*调用(需配合contextIsolation: true的contextBridge机制)。
示例 preload/index.js 片段:
// preload/index.js
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('electron', {
// 仅暴露必要方法
startVideo: (path) => ipcRenderer.invoke('play-video', path),
onVideoReady: (callback) => ipcRenderer.on('start-video', callback)
})? IPC 通信断裂的修复要点
原文提到“IPC 通信在修改配置后中断”,常见原因有三:
-
事件监听时机错位:
video.html对应的 React 应用(video.jsx)可能在secondWindow创建后才初始化,而ipcMain.on('play-video')已注册。需确保video.html加载完成、React 渲染完毕后再发送消息:// 在 video.jsx 中 useEffect(() => { window.electron.onVideoReady((event, videoPath) => { // 播放逻辑 }) }, []) -
窗口引用失效:
secondWindow可能被 GC 或关闭,ipcMain发送时未校验:ipcMain.on("play-video", (event, videoPath) => { if (secondWindow && !secondWindow.isDestroyed()) { secondWindow.webContents.send("start-video", videoPath) } }) ASAR 打包路径问题:若
video.html中硬编码了../public/xxx.json,在 ASAR 包内路径会变为app.asar/public/xxx.json,需用app.getAppPath()+join()动态拼接,或改用fetch('public/xxx.json')(Vite 会自动处理 public 目录)。
✅ 总结:四步闭环排查清单
| 步骤 | 检查项 | 工具/命令 |
|---|---|---|
| 1. 构建产物 |
out/renderer/ 下是否存在 video-*.html? |
ls out/renderer/ \| grep video |
| 2. 路径加载 |
secondWindow.loadFile(...) 是否指向构建后路径? |
console.log(join(__dirname, '../renderer/video.html')) |
| 3. 安全上下文 |
video.html 是否通过 preload 调用 Node API?是否禁用 eval? |
检查 DevTools → Application → Frames |
| 4. IPC 生命周期 |
secondWindow 是否存活?事件监听是否在组件挂载后注册? |
console.log(secondWindow?.isDestroyed()) |
遵循以上方案,即可彻底解决 Electron-Vite 多 HTML 页面在生产环境的加载失败问题,在保持高安全性(contextIsolation: true, webSecurity: true)前提下,实现主窗口与视频窗口的稳定协同。


















