[{"data":1,"prerenderedAt":656},["ShallowReactive",2],{"page-\u002Fjavascript\u002Felectron的指导":3},{"id":4,"title":5,"body":6,"description":12,"extension":646,"meta":647,"navigation":85,"path":652,"seo":653,"stem":654,"__hash__":655},"content\u002Fjavascript\u002Felectron的指导.md","Electron的指导",{"type":7,"value":8,"toc":643},"minimark",[9,13,17,20,23,26,303,306,309,444,447,578,581,593,604,607,610,613,616,619,628,636,639],[10,11,12],"p",{},"本笔记用于指导electron的理论和实践",[14,15,16],"h2",{"id":16},"架构",[10,18,19],{},"Electron 的 IPC是它的核心架构，主进程是一个node环境，用于管理窗口、声明周期、系统api，能够访问完整的nodejs api，比如说fs、path、child_process，不能直接操作dom，渲染进程是chromium环境，每个窗口一个，能够操作dom、跑前端代码，默认不能访问nodejs。",[10,21,22],{},"每个渲染进程在启动前，会加载一个预加载脚本，我们一般叫做preload.js，用于定义能访问的部分nodejs api，是主进程和渲染进程的bridge。",[10,24,25],{},"它提供的有三种模式，分别是渲染进程->主进程，有invoke和handle，渲染进程请求、主进程响应：",[27,28,33],"pre",{"className":29,"code":30,"language":31,"meta":32,"style":32},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F 主进程\nconst { ipcMain } = require('electron');\n\nipcMain.handle('read-file', async (event, filePath) => {\n    const content = await fs.promises.readFile(filePath, 'utf-8');\n    return content;   \u002F\u002F 返回给渲染进程\n});\n\n\u002F\u002F 预加载脚本\nconst { contextBridge, ipcRenderer } = require('electron');\n\ncontextBridge.exposeInMainWorld('api', {\n    readFile: (path) => ipcRenderer.invoke('read-file', path)\n});\n\n\u002F\u002F 渲染进程\nconst content = await window.api.readFile('\u002Fpath\u002Fto\u002Ffile');\n","js","",[34,35,36,45,80,87,128,157,169,175,180,186,213,218,235,264,269,274,280],"code",{"__ignoreMap":32},[37,38,41],"span",{"class":39,"line":40},"line",1,[37,42,44],{"class":43},"sJ8bj","\u002F\u002F 主进程\n",[37,46,48,52,56,60,63,66,70,73,77],{"class":39,"line":47},2,[37,49,51],{"class":50},"szBVR","const",[37,53,55],{"class":54},"sVt8B"," { ",[37,57,59],{"class":58},"sj4cs","ipcMain",[37,61,62],{"class":54}," } ",[37,64,65],{"class":50},"=",[37,67,69],{"class":68},"sScJk"," require",[37,71,72],{"class":54},"(",[37,74,76],{"class":75},"sZZnC","'electron'",[37,78,79],{"class":54},");\n",[37,81,83],{"class":39,"line":82},3,[37,84,86],{"emptyLinePlaceholder":85},true,"\n",[37,88,90,93,96,98,101,104,107,110,114,116,119,122,125],{"class":39,"line":89},4,[37,91,92],{"class":54},"ipcMain.",[37,94,95],{"class":68},"handle",[37,97,72],{"class":54},[37,99,100],{"class":75},"'read-file'",[37,102,103],{"class":54},", ",[37,105,106],{"class":50},"async",[37,108,109],{"class":54}," (",[37,111,113],{"class":112},"s4XuR","event",[37,115,103],{"class":54},[37,117,118],{"class":112},"filePath",[37,120,121],{"class":54},") ",[37,123,124],{"class":50},"=>",[37,126,127],{"class":54}," {\n",[37,129,131,134,137,140,143,146,149,152,155],{"class":39,"line":130},5,[37,132,133],{"class":50},"    const",[37,135,136],{"class":58}," content",[37,138,139],{"class":50}," =",[37,141,142],{"class":50}," await",[37,144,145],{"class":54}," fs.promises.",[37,147,148],{"class":68},"readFile",[37,150,151],{"class":54},"(filePath, ",[37,153,154],{"class":75},"'utf-8'",[37,156,79],{"class":54},[37,158,160,163,166],{"class":39,"line":159},6,[37,161,162],{"class":50},"    return",[37,164,165],{"class":54}," content;   ",[37,167,168],{"class":43},"\u002F\u002F 返回给渲染进程\n",[37,170,172],{"class":39,"line":171},7,[37,173,174],{"class":54},"});\n",[37,176,178],{"class":39,"line":177},8,[37,179,86],{"emptyLinePlaceholder":85},[37,181,183],{"class":39,"line":182},9,[37,184,185],{"class":43},"\u002F\u002F 预加载脚本\n",[37,187,189,191,193,196,198,201,203,205,207,209,211],{"class":39,"line":188},10,[37,190,51],{"class":50},[37,192,55],{"class":54},[37,194,195],{"class":58},"contextBridge",[37,197,103],{"class":54},[37,199,200],{"class":58},"ipcRenderer",[37,202,62],{"class":54},[37,204,65],{"class":50},[37,206,69],{"class":68},[37,208,72],{"class":54},[37,210,76],{"class":75},[37,212,79],{"class":54},[37,214,216],{"class":39,"line":215},11,[37,217,86],{"emptyLinePlaceholder":85},[37,219,221,224,227,229,232],{"class":39,"line":220},12,[37,222,223],{"class":54},"contextBridge.",[37,225,226],{"class":68},"exposeInMainWorld",[37,228,72],{"class":54},[37,230,231],{"class":75},"'api'",[37,233,234],{"class":54},", {\n",[37,236,238,241,244,247,249,251,254,257,259,261],{"class":39,"line":237},13,[37,239,240],{"class":68},"    readFile",[37,242,243],{"class":54},": (",[37,245,246],{"class":112},"path",[37,248,121],{"class":54},[37,250,124],{"class":50},[37,252,253],{"class":54}," ipcRenderer.",[37,255,256],{"class":68},"invoke",[37,258,72],{"class":54},[37,260,100],{"class":75},[37,262,263],{"class":54},", path)\n",[37,265,267],{"class":39,"line":266},14,[37,268,174],{"class":54},[37,270,272],{"class":39,"line":271},15,[37,273,86],{"emptyLinePlaceholder":85},[37,275,277],{"class":39,"line":276},16,[37,278,279],{"class":43},"\u002F\u002F 渲染进程\n",[37,281,283,285,287,289,291,294,296,298,301],{"class":39,"line":282},17,[37,284,51],{"class":50},[37,286,136],{"class":58},[37,288,139],{"class":50},[37,290,142],{"class":50},[37,292,293],{"class":54}," window.api.",[37,295,148],{"class":68},[37,297,72],{"class":54},[37,299,300],{"class":75},"'\u002Fpath\u002Fto\u002Ffile'",[37,302,79],{"class":54},[10,304,305],{},"它的特点是promise风格，异步有返回值。",[10,307,308],{},"另一种是渲染进程->主进程，使用send和on完成，是单向的，渲染进程发，主进程收，但是不返回。",[27,310,312],{"className":29,"code":311,"language":31,"meta":32,"style":32},"\u002F\u002F 主进程\nipcMain.on('log', (event, message) => {\n    console.log('渲染进程说:', message);\n    \u002F\u002F 不返回\n});\n\n\u002F\u002F 预加载\ncontextBridge.exposeInMainWorld('api', {\n    log: (msg) => ipcRenderer.send('log', msg)\n});\n\n\u002F\u002F 渲染进程\nwindow.api.log('hello');\n",[34,313,314,318,346,362,367,371,375,380,392,418,422,426,430],{"__ignoreMap":32},[37,315,316],{"class":39,"line":40},[37,317,44],{"class":43},[37,319,320,322,325,327,330,333,335,337,340,342,344],{"class":39,"line":47},[37,321,92],{"class":54},[37,323,324],{"class":68},"on",[37,326,72],{"class":54},[37,328,329],{"class":75},"'log'",[37,331,332],{"class":54},", (",[37,334,113],{"class":112},[37,336,103],{"class":54},[37,338,339],{"class":112},"message",[37,341,121],{"class":54},[37,343,124],{"class":50},[37,345,127],{"class":54},[37,347,348,351,354,356,359],{"class":39,"line":82},[37,349,350],{"class":54},"    console.",[37,352,353],{"class":68},"log",[37,355,72],{"class":54},[37,357,358],{"class":75},"'渲染进程说:'",[37,360,361],{"class":54},", message);\n",[37,363,364],{"class":39,"line":89},[37,365,366],{"class":43},"    \u002F\u002F 不返回\n",[37,368,369],{"class":39,"line":130},[37,370,174],{"class":54},[37,372,373],{"class":39,"line":159},[37,374,86],{"emptyLinePlaceholder":85},[37,376,377],{"class":39,"line":171},[37,378,379],{"class":43},"\u002F\u002F 预加载\n",[37,381,382,384,386,388,390],{"class":39,"line":177},[37,383,223],{"class":54},[37,385,226],{"class":68},[37,387,72],{"class":54},[37,389,231],{"class":75},[37,391,234],{"class":54},[37,393,394,397,399,402,404,406,408,411,413,415],{"class":39,"line":182},[37,395,396],{"class":68},"    log",[37,398,243],{"class":54},[37,400,401],{"class":112},"msg",[37,403,121],{"class":54},[37,405,124],{"class":50},[37,407,253],{"class":54},[37,409,410],{"class":68},"send",[37,412,72],{"class":54},[37,414,329],{"class":75},[37,416,417],{"class":54},", msg)\n",[37,419,420],{"class":39,"line":188},[37,421,174],{"class":54},[37,423,424],{"class":39,"line":215},[37,425,86],{"emptyLinePlaceholder":85},[37,427,428],{"class":39,"line":220},[37,429,279],{"class":43},[37,431,432,435,437,439,442],{"class":39,"line":237},[37,433,434],{"class":54},"window.api.",[37,436,353],{"class":68},[37,438,72],{"class":54},[37,440,441],{"class":75},"'hello'",[37,443,79],{"class":54},[10,445,446],{},"主进程单向到渲染进程：",[27,448,450],{"className":29,"code":449,"language":31,"meta":32,"style":32},"\u002F\u002F 主进程\nmainWindow.webContents.send('update', { version: '1.0.0' });\n\u002F\u002F 预加载\ncontextBridge.exposeInMainWorld('api', {\n    onUpdate: (callback) => ipcRenderer.on('update', (event, data) => callback(data))\n});\n\u002F\u002F 渲染进程\nwindow.api.onUpdate((data) => {\n    console.log('收到更新:', data);\n});\n",[34,451,452,456,477,481,493,534,538,542,560,574],{"__ignoreMap":32},[37,453,454],{"class":39,"line":40},[37,455,44],{"class":43},[37,457,458,461,463,465,468,471,474],{"class":39,"line":47},[37,459,460],{"class":54},"mainWindow.webContents.",[37,462,410],{"class":68},[37,464,72],{"class":54},[37,466,467],{"class":75},"'update'",[37,469,470],{"class":54},", { version: ",[37,472,473],{"class":75},"'1.0.0'",[37,475,476],{"class":54}," });\n",[37,478,479],{"class":39,"line":82},[37,480,379],{"class":43},[37,482,483,485,487,489,491],{"class":39,"line":89},[37,484,223],{"class":54},[37,486,226],{"class":68},[37,488,72],{"class":54},[37,490,231],{"class":75},[37,492,234],{"class":54},[37,494,495,498,500,503,505,507,509,511,513,515,517,519,521,524,526,528,531],{"class":39,"line":130},[37,496,497],{"class":68},"    onUpdate",[37,499,243],{"class":54},[37,501,502],{"class":112},"callback",[37,504,121],{"class":54},[37,506,124],{"class":50},[37,508,253],{"class":54},[37,510,324],{"class":68},[37,512,72],{"class":54},[37,514,467],{"class":75},[37,516,332],{"class":54},[37,518,113],{"class":112},[37,520,103],{"class":54},[37,522,523],{"class":112},"data",[37,525,121],{"class":54},[37,527,124],{"class":50},[37,529,530],{"class":68}," callback",[37,532,533],{"class":54},"(data))\n",[37,535,536],{"class":39,"line":159},[37,537,174],{"class":54},[37,539,540],{"class":39,"line":171},[37,541,279],{"class":43},[37,543,544,546,549,552,554,556,558],{"class":39,"line":177},[37,545,434],{"class":54},[37,547,548],{"class":68},"onUpdate",[37,550,551],{"class":54},"((",[37,553,523],{"class":112},[37,555,121],{"class":54},[37,557,124],{"class":50},[37,559,127],{"class":54},[37,561,562,564,566,568,571],{"class":39,"line":182},[37,563,350],{"class":54},[37,565,353],{"class":68},[37,567,72],{"class":54},[37,569,570],{"class":75},"'收到更新:'",[37,572,573],{"class":54},", data);\n",[37,575,576],{"class":39,"line":188},[37,577,174],{"class":54},[10,579,580],{},"ipc的底层是chromium的mojo ipc，mojo是chromium的ipc框架，管理二进制序列化、异步、跨进程，electron封装了mojo，能够提供ipcRenderer、ipcMain。",[10,582,583,584,587,588,592],{},"mojo在win32平台的底层传输是基于命名管道实现的，函数在 Windows 下会调用 ",[34,585,586],{},"CreateNamedPipeW"," 来创建管道。一个专门抓取 Chromium IPC 的工具也明确指出，它捕获的就是 ",[589,590,591],"strong",{},"Named Pipe"," 的流量",[10,594,595,596,599,600,603],{},"值得一提的是，从 Chrome 112 开始，Chromium 逐渐用新的 ",[589,597,598],{},"ipcz"," 协议替代了 Mojo Core。ipcz 为了优化性能，在 Windows 上可能会更多地使用",[589,601,602],{},"共享内存","来传递数据，而不是完全依赖命名管道，但这属于更底层的优化实现。",[10,605,606],{},"安全方面：",[10,608,609],{},"contextIsolation是默认开启的，渲染进程和预加载脚本上下文隔离。nodeInteration也是默认关闭的，渲染进程肯定不能访问node。sandbox沙箱默认开启，渲染进程在沙箱里。webSecurit默认开启，同源策略。",[10,611,612],{},"生命周期方面：",[10,614,615],{},"app.whenReady()是app初始化完成的时候，创建窗口。\nwindow-all-closed是所有窗口都关闭，macos不退出。\nbefore-quit: 退出前，可以拦截\nwill-quit:即将推出\nquit:已退出\nready:app就绪\nsecond-instance:第二个实例启动",[10,617,618],{},"窗口管理：",[10,620,621,622,624,625],{},"BrowserWindow:创建窗口\nwebPreferences:安全配置preload、contextisloation、nodeintergration\nloadURL\u002FloadFile：加载远程URL\u002F本地文件\nready-to-show:窗口准备好显示，避免白屏\nframe:false:无边框窗口，自定义标题栏\nshow:false + read-to-show 先隐藏，准备好在显示，可以消除白屏\ntransparent:true透明窗口\nalwaysOnTop：置顶\nwebContents:窗口的web内容，可以",[34,623,410],{},"、",[34,626,627],{},"openDevTools",[10,629,630,631,624,633,635],{},"preload脚本是渲染进程启动前，DOM加载前执行，能够访问",[34,632,195],{},[34,634,200],{},"、部分 Node API。",[10,637,638],{},"继续更新",[640,641,642],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":32,"searchDepth":47,"depth":47,"links":644},[645],{"id":16,"depth":47,"text":16},"md",{"date":648,"tags":649},"2026-12-10",[650,651],"javascript","electron","\u002Fjavascript\u002Felectron的指导",{"description":12},"javascript\u002Felectron的指导","Cmv2YCpuSJIEdpfLcIPjsCC_yTzlRUH4BDClk7seoMs",1791871181126]