mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-23 06:25:41 -05:00
101 lines
2.9 KiB
Markdown
101 lines
2.9 KiB
Markdown
---
|
|
tags: [electron, ipc, preload, contextBridge, security]
|
|
session: 4
|
|
duration: 2 hours
|
|
status: ready
|
|
---
|
|
|
|
# Session 4 — IPC: Making the Two Processes Talk
|
|
|
|
## 🎯 Objectives
|
|
- Understand why IPC exists and why it's necessary
|
|
- Send messages from Renderer → Main and back
|
|
- Use `preload.js` and `contextBridge` safely
|
|
- Wire a full two-way communication flow
|
|
|
|
---
|
|
|
|
## 🧠 Concepts Covered
|
|
|
|
### Why IPC?
|
|
The Renderer runs in a sandboxed browser context.
|
|
It cannot directly call `fs.readFile()` or access Node.js.
|
|
IPC is the secure message-passing bridge.
|
|
```
|
|
Renderer → ipcRenderer.send('open-file') →
|
|
Main → ipcMain.on('open-file', handler) →
|
|
Main → win.webContents.send('file-content', data) →
|
|
Renderer → ipcRenderer.on('file-content', handler)
|
|
```
|
|
|
|
### The Secure Way: preload.js + contextBridge
|
|
```js
|
|
// preload.js — runs in a special context with BOTH Node and DOM access
|
|
const { contextBridge, ipcRenderer } = require('electron')
|
|
|
|
contextBridge.exposeInMainWorld('electronAPI', {
|
|
openFile: () => ipcRenderer.invoke('open-file'),
|
|
saveFile: (content) => ipcRenderer.invoke('save-file', content),
|
|
onFileLoaded: (callback) => ipcRenderer.on('file-loaded', callback)
|
|
})
|
|
```
|
|
```js
|
|
// main.js — handles the requests
|
|
const { ipcMain, dialog } = require('electron')
|
|
const fs = require('fs')
|
|
|
|
ipcMain.handle('open-file', async () => {
|
|
const result = await dialog.showOpenDialog({ ... })
|
|
if (!result.canceled) {
|
|
return fs.readFileSync(result.filePaths[0], 'utf-8')
|
|
}
|
|
})
|
|
|
|
ipcMain.handle('save-file', async (event, content) => {
|
|
const result = await dialog.showSaveDialog({})
|
|
if (!result.canceled) {
|
|
fs.writeFileSync(result.filePath, content)
|
|
return true
|
|
}
|
|
})
|
|
```
|
|
```js
|
|
// renderer (index.html script) — uses the exposed API
|
|
document.getElementById('open-btn').addEventListener('click', async () => {
|
|
const content = await window.electronAPI.openFile()
|
|
document.getElementById('editor').value = content
|
|
})
|
|
```
|
|
|
|
### invoke vs send
|
|
| `ipcRenderer.send` | `ipcRenderer.invoke` |
|
|
|--------------------|----------------------|
|
|
| Fire and forget | Returns a Promise |
|
|
| One-way | Two-way (request/response) |
|
|
| Use for events | Use for data fetching |
|
|
|
|
---
|
|
|
|
## 🛠 Hands-on Exercise — Wire the Notepad (60 min)
|
|
|
|
Complete the Notepad from Session 3 using proper IPC:
|
|
|
|
1. Set up `preload.js` with `contextBridge`
|
|
2. Add `webPreferences: { preload: ... }` to BrowserWindow
|
|
3. Expose `openFile` and `saveFile` via `electronAPI`
|
|
4. Wire up Open and Save buttons in the renderer
|
|
5. **Bonus:** Add a word count display that updates as you type
|
|
|
|
---
|
|
|
|
## ⚠️ Common Mistakes
|
|
- Forgetting to set `preload` in `webPreferences`
|
|
- Using `nodeIntegration: true` (insecure, avoid it)
|
|
- Trying to `require('fs')` in renderer directly
|
|
|
|
---
|
|
|
|
## 📦 Resources
|
|
- [IPC Docs](https://www.electronjs.org/docs/latest/tutorial/ipc)
|
|
- [contextBridge Docs](https://www.electronjs.org/docs/latest/api/context-bridge)
|
|
- [[Session 5 - App Design & WebSockets Intro]] ← next |