应使用 app:// 协议注册自定义资源路径,因其规避 file:// 跨目录拦截、适配 .asar 打包、支持多层目录,需在 app.whenReady() 后、BrowserWindow 创建前调用 protocol.registerFileProtocol('app', ...)。

直接用 大概率会静默失败,不是路径写错,而是 Chromium 主动拦截——必须明确协议行为、打包影响和加载时机。
为什么 href="./styles/main.css" 有时报 “Not allowed to load local resource”
这不是 404,是 Chromium 在 webSecurity: true(Electron 5+ 默认启用)下对 file:// 协议的跨目录请求做的协议级拒绝。哪怕 fs.existsSync() 返回 true,浏览器根本不会发请求,控制台只显示安全错误。
- 开发时“偶尔能用”,往往因为 HTML 和 CSS 恰好同级,被宽松放行;一旦挪到子目录(如
windows/secondary.html),相对路径基准就变了 -
win.loadURL('file://' + path.join(...))会让 Chromium 丢失当前目录上下文,./彻底失效 - CSS 文件里用了
@import './theme.css'?在.asar中常因路径解析失败而静默丢弃,不报错也不加载
用 app:// 协议注册自定义资源路径是最稳方案
它规避 file:// 的跨目录拦截、适配 .asar 打包、支持多层目录结构,是唯一可扩展的长期解法。
- 必须在
app.whenReady()之后、BrowserWindow创建之前调用protocol.registerFileProtocol('app', ...) - 回调中必须用
path.normalize()处理request.url.slice(6)拼出的路径,否则可能被..遍历攻击 - HTML 中写
,不是file:// - 路径映射逻辑统一收口,后续加新样式、换目录结构都不用改 HTML
用 preload.js 注入 CSS 字符串适合运行时控制场景
绕过所有协议限制,但只适合小量样式、主题切换或需动态生成的 CSS,不能替代主样式表。
- 必须在
BrowserWindow的webPreferences中启用preload,且推荐保持contextIsolation: true - 路径要用
path.join(__dirname, '../styles/main.css')—— 注意:__dirname是preload.js所在目录,不是 HTML 所在目录 - 打包后必须用
process.resourcesPath或app.getAppPath()替代__dirname,否则读不到.asar内部文件 - 注入时机必须等
document.addEventListener('DOMContentLoaded', ...),不能在 preload 脚本顶部执行 - 大体积 CSS 建议用
readFileSync(..., 'utf8').trim()清掉 BOM,避免解析失败
真正麻烦的不是“怎么加一行 link”,而是当项目从单窗口变成多窗口、从开发态进入打包态、从 flat 目录演进为嵌套结构时,路径基准和资源可见性会层层坍塌。这时候靠手调相对路径只会越修越乱,app:// 协议注册才是把不确定性收束到主进程的务实做法。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











