0Pricing
Electron Desktop App Development · บทเรียน

สถาปัตยกรรมหลายหน้าต่าง

ออกแบบและสร้างแอปพลิเคชัน Electron หลายหน้าต่างที่ซับซ้อน พร้อมจัดการการสื่อสารระหว่างหน้าต่างและสถานะอย่างมีประสิทธิภาพ

สถาปัตยกรรมหลายหน้าต่าง เป็นบทเรียน Electron Desktop App Development ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Electron Desktop App Development และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Electron Desktop App Development มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

Why Multiple Windows?

Electron applications often benefit from using multiple windows. Think of a chat application: you might have a main contact list and separate windows for each active conversation.

  • Separate Workflows: Isolate tasks into dedicated windows.
  • User Preferences: A settings window distinct from the main application.
  • Auxiliary Tools: Dedicated viewers, inspectors, or side panels.
  • Enhanced User Experience: Provides flexibility and organization for complex apps.

Spawning a New Window

Creating additional windows in Electron is similar to creating your initial main window. You simply instantiate another BrowserWindow in your main process.

Here's how you can create a second window that loads a different HTML file.

const { app, BrowserWindow } = require('electron');
const path = require('path');

let mainWindow;
let secondWindow;

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js')
    }
  });
  mainWindow.loadFile('index.html');
}

function createSecondWindow() {
  secondWindow = new BrowserWindow({
    width: 400,
    height: 300,
    parent: mainWindow, // Optional: make it a child window
    modal: false, // Optional: for modal behavior
    show: false, // Don't show immediately
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload_second.js')
    }
  });
  secondWindow.loadFile('second.html');
  secondWindow.once('ready-to-show', () => {
    secondWindow.show();
  });
}

app.whenReady().then(() => {
  createMainWindow();
  createSecondWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createMainWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

Content for Multiple Windows

Each BrowserWindow instance loads its own content, typically an HTML file. This allows you to design completely independent user interfaces for different parts of your application.

For the previous example, you would need:

  • index.html: The main window's interface.
  • second.html: The second window's distinct interface.
  • Corresponding renderer and preload scripts for each, if needed.

Managing Window References

When working with multiple windows, it's crucial to keep track of their references. You can store them in an array or a map, allowing you to interact with specific windows later.

This helps in sending targeted messages, closing specific windows, or managing their states.

const { app, BrowserWindow } = require('electron');
const path = require('path');

let allWindows = []; // Array to hold references to all windows

function createNewWindow(htmlFile, width, height) {
  let newWindow = new BrowserWindow({
    width: width,
    height: height,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js') // Can be different per window
    }
  });
  newWindow.loadFile(htmlFile);
  allWindows.push(newWindow); // Add to our list

  // Remove from list when closed
  newWindow.on('closed', () => {
    allWindows = allWindows.filter(win => win !== newWindow);
  });

  return newWindow;
}

app.whenReady().then(() => {
  createNewWindow('index.html', 800, 600); // Create main window
  createNewWindow('second.html', 400, 300); // Create a second window

  // Example: Accessing windows later
  // allWindows[0].setTitle('Main App');
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

Main to Specific Renderer IPC

The main process can send messages to a specific renderer process using the webContents.send() method of that window instance.

This is essential for updating UI elements, pushing data, or triggering actions in a particular window.

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

let mainWindow;
let settingsWindow;

function createWindows() {
  mainWindow = new BrowserWindow({
    width: 800, height: 600,
    webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true }
  });
  mainWindow.loadFile('index.html');

  settingsWindow = new BrowserWindow({
    width: 400, height: 300, show: false,
    webPreferences: { preload: path.join(__dirname, 'preload_settings.js'), contextIsolation: true }
  });
  settingsWindow.loadFile('settings.html');
}

app.whenReady().then(createWindows);

// Main process sends message to settings window
ipcMain.on('open-settings', () => {
  if (settingsWindow) {
    settingsWindow.show();
    settingsWindow.webContents.send('settings-opened', 'Welcome to settings!');
  }
});

// --- Renderer (preload_settings.js) for settings.html ---
// const { ipcRenderer, contextBridge } = require('electron');
// contextBridge.exposeInMainWorld('electronAPI', {
//   onSettingsOpened: (callback) => ipcRenderer.on('settings-opened', (event, message) => callback(message))
// });

// --- Renderer (settings.html script) ---
// window.electronAPI.onSettingsOpened((msg) => {
//   document.getElementById('message').innerText = msg;
// });

Renderer to Main (Identifying Sender)

When a renderer process sends a message to the main process via ipcRenderer.send(), the main process receives an event object.

This event object contains information about the sender, including event.senderFrame or event.sender (which is the WebContents object of the sending window). This allows the main process to identify which window sent the message.

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

let windows = {}; // Store windows by an ID or name

function createWindows() {
  const mainWin = new BrowserWindow({
    width: 800, height: 600, title: 'Main',
    webPreferences: { preload: path.join(__dirname, 'preload_main.js'), contextIsolation: true }
  });
  mainWin.loadFile('main.html');
  windows['main'] = mainWin;

  const toolWin = new BrowserWindow({
    width: 400, height: 300, title: 'Tool',
    webPreferences: { preload: path.join(__dirname, 'preload_tool.js'), contextIsolation: true }
  });
  toolWin.loadFile('tool.html');
  windows['tool'] = toolWin;
}

app.whenReady().then(createWindows);

ipcMain.on('renderer-message', (event, data) => {
  const senderWindow = BrowserWindow.fromWebContents(event.sender);
  const senderId = Object.keys(windows).find(key => windows[key] === senderWindow);
  console.log(`Message from ${senderId || 'Unknown'}: ${data}`);
  senderWindow.webContents.send('main-reply', `Received from ${senderId}!`);
});

// --- preload_main.js / preload_tool.js (simplified) ---
// const { ipcRenderer, contextBridge } = require('electron');
// contextBridge.exposeInMainWorld('electronAPI', {
//   sendMessage: (data) => ipcRenderer.send('renderer-message', data),
//   onMainReply: (callback) => ipcRenderer.on('main-reply', (event, msg) => callback(msg))
// });

// --- main.html / tool.html script ---
// window.electronAPI.sendMessage('Hello from my window!');

Renderer to Renderer Communication

Renderer processes cannot directly communicate with each other. All inter-process communication (IPC) must be mediated by the main process.

This means a message from one renderer will go to the main process, which then relays it to the target renderer process. This centralizes communication and enhances security.

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

let windowA, windowB;

function createWindows() {
  windowA = new BrowserWindow({
    width: 600, height: 400, title: 'Window A',
    webPreferences: { preload: path.join(__dirname, 'preload_a.js'), contextIsolation: true }
  });
  windowA.loadFile('window_a.html');

  windowB = new BrowserWindow({
    width: 600, height: 400, title: 'Window B',
    webPreferences: { preload: path.join(__dirname, 'preload_b.js'), contextIsolation: true }
  });
  windowB.loadFile('window_b.html');
}

app.whenReady().then(createWindows);

// Renderer A sends to Main, Main relays to Renderer B
ipcMain.on('message-from-a', (event, message) => {
  console.log('Main received from A:', message);
  if (windowB) {
    windowB.webContents.send('message-to-b', `Relayed from A: ${message}`);
  }
});

// Renderer B sends to Main, Main relays to Renderer A
ipcMain.on('message-from-b', (event, message) => {
  console.log('Main received from B:', message);
  if (windowA) {
    windowA.webContents.send('message-to-a', `Relayed from B: ${message}`);
  }
});

// --- preload_a.js (example) ---
// const { ipcRenderer, contextBridge } = require('electron');
// contextBridge.exposeInMainWorld('electronAPI', {
//   sendToB: (msg) => ipcRenderer.send('message-from-a', msg),
//   onMessageFromB: (callback) => ipcRenderer.on('message-to-a', (event, msg) => callback(msg))
// });

// --- window_a.html script ---
// window.electronAPI.sendToB('Hello from Window A!');

Managing Shared State

When you have multiple windows, they often need to access or share the same data. Here are common strategies:

  • Main Process as Source of Truth: Store shared data in the main process and use IPC to request/update it from renderers.
  • Electron Store: A simple, cross-platform solution for persisting user settings and application state.
  • IPC for Data Sync: Renderers notify the main process of changes, and the main process broadcasts updates to other affected renderers.
  • Global Object (Careful!): In some simple cases, a global JavaScript object in the main process can hold shared state, but this can get messy in complex apps.

Multi-Window Best Practices

Designing multi-window applications requires thoughtful consideration:

  • Memory Usage: Each BrowserWindow is a separate Chromium instance, consuming memory. Minimize unnecessary windows.
  • Lifecycle Management: Decide if closing a child window should affect its parent or the main application.
  • User Experience: Provide clear navigation between windows. Consider window positioning and remember user preferences.
  • Error Handling: Implement robust error handling for IPC and window events to prevent crashes.
  • Context Isolation: Always enable contextIsolation and use preload scripts for secure API exposure, especially with multiple windows.

Multi-Window IPC Check

You have two renderer processes, Renderer A and Renderer B. Renderer A needs to send a message to Renderer B. How should this communication be structured in Electron?

Recap: Multi-Window Architectures

Congratulations! You've learned how to design and implement multi-window Electron applications.

We covered creating multiple BrowserWindow instances, managing their references, and implementing secure inter-window communication via the main process. You also explored strategies for shared state and best practices for building complex, multi-window desktop experiences.

คำถามที่พบบ่อย

บทเรียน “สถาปัตยกรรมหลายหน้าต่าง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “สถาปัตยกรรมหลายหน้าต่าง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Electron Desktop App Development ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Electron Desktop App Development มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “สถาปัตยกรรมหลายหน้าต่าง”

ออกแบบและสร้างแอปพลิเคชัน Electron หลายหน้าต่างที่ซับซ้อน พร้อมจัดการการสื่อสารระหว่างหน้าต่างและสถานะอย่างมีประสิทธิภาพ คุณปฏิบัติ Electron Desktop App Development ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Electron Desktop App Development หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Electron Desktop App Development บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “สถาปัตยกรรมหลายหน้าต่าง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Electron Desktop App Development นี้ได้ไหม

ได้ บทเรียน Electron Desktop App Development ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. สถาปัตยกรรมหลายหน้าต่าง
  2. กระบวนการเบื้องหลังและตัวทำงาน
  3. การผสานรวมกับบริการคลาวด์
  4. การอัปเดตแอป Electron อัตโนมัติ
← กลับไปที่ Electron Desktop App Development