Tạo Ứng Dụng Desktop Với Electron: Hướng Dẫn Thiết Lập Nhanh

1. Thiết Lập Dự Án Cơ Bản

Tạo thư mục và khởi tạo dự án:

mkdir my-electron-app && cd my-electron-app
npm init -y

Chú ý: Should fill in the author and description fields in package.json; leaving them blank can cause packaging failures.

Cập nhật thông tin package.json với entry point là main.js:

{
  "name": "electron-app",
  "version": "1.0.0",
  "description": "Ứng dụng electron đầu tiên",
  "main": "main.js",
  "scripts": {
    "start": "electron ."
  },
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "electron": "^31.2.0"
  }
}

Cài đặt Electron:

npm install --save-dev electron

Tạo tập tin main.js cơ bản trong thư mục gốc:

console.log('Electron session started');

Khởi chạy với:

npm run start

Nếu thấy tin nhắn trên xuất hiện trên console, module Electron đã hoạt động.

2. Tạo Cửa Sổ Hiển Thị Nội Dung

Tạo cửa sổ trình duyệt để hiển thị nội dung web bên ngoài.

// main.js
const { app, BrowserWindow } = require('electron');

app.on('ready', () => {
  const mainWindow = new BrowserWindow({
    width: 900,
    height: 650,
    autoHideMenuBar: true,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true
    }
  });

  mainWindow.loadURL('https://example.com/');
});

Nhấn Ctrl+Shift+I để bật Inspector (thanh công cụ phát triển). Dùng Ctrl+R để làm mới trang.

3. Hiển Thị Trang HTML Cục Bộ

Đặt file HTML và CSS trong thư mục pages/.

pages/index.html:

<html>
  <head><meta charset="UTF-8">
    <title>Trang Electron</title>
  </head>
  <body><h1>Trang chủ Electron</h1>
    <script src="./render.js"></script>
  </body>
</html>

Cập nhật main.js để tải tập tin địa phương:

mainWindow.loadFile('./pages/index.html');

4. Các Sự Kiện Lifecycle

Sử dụng app.on() để lắng nghe các sự kiện đặc trưng:

  • ready: Khi Electron hoàn tất khởi tạo.
  • window-all-closed: Khi tất cả các cửa sổ đã đóng.
  • activate: Khi người dùng kích hoạt ứng dụng (macOS).
  • before-quit / will-quit / quit: Giai đoạn đóng ứng dụng.

5. Xử Lý Trạng Thái Mạng

Xử lý khi kết nối mạng thay đổi trong quá trình chạy ứng dụng:

// Trong render.js
window.addEventListener('online', () => console.log('Đã kết nối mạng'));
window.addEventListener('offline', () => console.log('Mất kết nối mạng'));

Ví dụ hiển thị thông báo đơn giản:

alert('Mất kết nối mạng!');

6. Giao Tiếp Giữa Các Quá Trình

Để an toàn, Electron tách biệt môi trường Node.js và trình duyệt. Có hai cách tiếp cận:

Cách 1: Cho Phép Tích Hợp Node

Trong main.js ở phần webPreferences:

webPreferences: {
  nodeIntegration: true,
  contextIsolation: false
}

Sau đó, trong render.js, dùng Node API trực tiếp:

const fs = require('fs');

document.getElementById('saveBtn').addEventListener('click', () => {
  const content = document.getElementById('input').value;
  fs.writeFileSync('D:/note.txt', content);
});

Cách 2: Dùng Preload Script (Đề Xuất Ra)

Đặt preloaded script:

// preload.js
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('api', {
  saveData: (data) => ipcRenderer.send('save', data),
  fetchData: () => ipcRenderer.invoke('read')
});

Cấu hình trong main.js:

webPreferences: {
  preload: path.join(__dirname, 'preload.js')
}

Xử lý IPC trong chính quá trình chủ:

const { ipcMain } = require('electron');
const fs = require('fs');

ipcMain.handle('read', () => fs.readFileSync('D:/note.txt', 'utf8'));
ipcMain.on('save', (_, data) => fs.writeFileSync('D:/note.txt', data));

Gọi từ side rendering:

api.saveData(inputValue);
const saved = await api.fetchData();

7. Tạo Cửa Sổ Phụ

Tạo cửa sổ mới khi người dùng bấm nút:

// trong render.js
document.getElementById('openChild').addEventListener('click', () => {
  const win = new BrowserWindow({
    width: 500,
    height: 400,
    parent: remote.getCurrentWindow()
  });
  win.loadURL('https://subpage.example.com');
});

Lưu ý: Cần cấu hình enableRemoteModule: true nếu dùng phiên bản Electron mới, hoặc thay bằng IPC.

8. System Menu & Context Menu

Tạo menu trong ứng dụng:

// menu.js
const { Menu } = require('electron');

const template = [
  {
    label: '"Công cụ"',
    submenu: [
      { label: '"Mở thiết bị phát triển"', accelerator: 'Ctrl+Shift+I', click(item, win) {
        win.webContents.openDevTools();
      }},
      { type: 'separator' },
      { label: '"Thoát"', role: 'quit' }
    ]
  }
];

module.exports = Menu.buildFromTemplate(template);

Kết nối trong main.js:

const menu = require('./menu');
Menu.setApplicationMenu(menu);

Menu chuột phải:

const { remote, Menu } = require('electron');
window.addEventListener('contextmenu', (e) => {
  e.preventDefault();
  const ctxMenu = Menu.buildFromTemplate([
    { label: '"Bản sao"', role: 'copy' },
    { label: '"Dán"', role: 'paste' },
    { type: 'separator' },
    { label: '"Mở Inspector"', click: () => remote.getCurrentWindow().webContents.openDevTools() }
  ]);
  ctxMenu.popup({ window: remote.getCurrentWindow() });
});

9. Shortcut Key Toàn Cục

Đăng ký phím nóng toàn hệ thống:

const { globalShortcut } = require('electron');

app.on('ready', () => {
  globalShortcut.register('Ctrl+Shift+F', () => {
    console.log('Tìm kiếm toàn ứng dụng đã kích hoạt');
  });
});

app.on('will-quit', () => {
  globalShortcut.unregisterAll();
});

10. Các Tính Năng Hệ Thống Khác

  • Clipboard: dùng remote.clipboard.writeText() hoặc clipboard module.
  • Open url trong trình duyệt mặc định: shell.openExternal(url).
  • Dialog: mở cửa sổ chọn file, lưu file, messagebox với dialog.showOpenDialog(), dialog.showSaveDialog(), dialog.showMessageBox().

11. Tự Động Tái Khởi Động Khi Phát Triển

Cài đặt nodemon và cấu hình để tự động khởi động lại khi có thay đổi:

npm install --save-dev nodemon

Cập nhật script trong package.json:

"scripts": {
  "start": "nodemon --exec electron ."
}

Hoặc dùng file cấu hình nodemon.json để kiểm soát chi tiết hơn:

{
  "watch": ["src", "main.js", "renderer.js"],
  "ext": "js,json,html,css"
}

12. Đóng Gói Ứng Dụng

Cài đặt electron-builder:

npm install --save-dev electron-builder

Cấu hình trong package.json at build field:

"build": {
  "appId": "com.example.myapp",
  "asar": true,
  "win": {
    "target": ["nsis"],
    "icon": "build/icon.ico"
  },
  "nsis": {
    "oneClick": false,
    "perMachine": true,
    "createDesktopShortcut": true
  }
}

Thêm dòng script:

"scripts": {
  "pack": "electron-builder --dir",
  "dist": "electron-builder"
}

Chạy lệnh tạo build cuối:

npm run dist

Kết Luận

Electron cho phép xây dựng ứng dụng desktop đa nền tảng nhanh chóng với kiến trúc web quen thuộc. Hãy ưu tiên IPC thay vì remote để bảo mật cao hơn và hạn chế rủi ro trong môi trường production.

Thẻ: electron ipcRenderer BrowserWindow preload-script nodeIntegration

Đăng vào ngày 10 tháng 6 lúc 22:12