Ukratko

Najvažnije iz članka

  • Electron omogućuje izradu cross-platform desktop aplikacija (Windows, macOS, Linux) koristeći web tehnologije (HTML, CSS, JavaScript/TypeScript).
  • TypeScript donosi statičko tipiziranje, što poboljšava skalabilnost, održivost i detekciju grešaka u Electron projektima.
  • Electron aplikacije imaju glavni (Node.js) i renderer (Chromium) proces koji komuniciraju putem IPC-ja; sigurnost je ključna (contextIsolation, nodeIntegration: false).
  • Postavljanje uključuje Node.js, Electron, TypeScript, konfiguraciju `tsconfig.json`, te pakiranje aplikacije pomoću alata poput `electron-builder` za distribuciju.
Sadržaj članka
  1. Što je Electron?
  2. Zašto koristiti Electron i TypeScript?
  3. Arhitektura Electron Aplikacije
  4. Postavljanje Razvojnog Okruženja
  5. Pisanje Koda za Glavni Proces (main.ts)
  6. Pisanje Koda za Predopterećenje (preload.ts)
  7. Pisanje Koda za Renderer Proces (renderer.ts)
  8. HTML za Korisničko Sučelje (public/index.html)
  9. CSS Stilovi (public/styles.css)
  10. Komunikacija Između Procesora (IPC)
  11. Pokretanje Aplikacije
  12. Pakiranje Aplikacije
  13. Napredne Teme i Best Practices
  14. Zaključak

U današnjem svijetu, gdje se korisnici sve više oslanjaju na desktop aplikacije za produktivnost, zabavu i specifične zadatke, mogućnost brzog i učinkovitog razvoja takvih aplikacija je ključna. Tradicionalno, izrada desktop aplikacija zahtijeva poznavanje specifičnih jezika i frameworka za svaku platformu (npr. C#/.NET za Windows, Objective-C/Swift za macOS, C++/Qt za Linux). Međutim, s pojavom projekata poput Electrona, ta je barijera značajno smanjena, omogućujući web developerima da iskoriste svoje postojeće vještine za izgradnju bogatih desktop iskustava.

Što je Electron?

Electron je open-source framework koji je razvio GitHub, a omogućuje izradu desktop GUI aplikacija koristeći web tehnologije. To znači da možete pisati aplikacije s HTML-om, CSS-om i JavaScriptom (ili TypeScriptom) koje se onda "pakiraju" u samostalnu desktop aplikaciju za Windows, macOS i Linux. Srž Electrona čini kombinacija Chromium rendering enginea (istog onog koji pokreće Google Chrome preglednik) za prikaz korisničkog sučelja i Node.js runtimea za pristup sistemskim resursima i obavljanje pozadinskih zadataka. Ova arhitektura omogućuje aplikacijama da izgledaju i funkcioniraju poput nativnih, istovremeno nudeći fleksibilnost i brzinu razvoja karakterističnu za web.

Neke od najpoznatijih aplikacija izgrađenih pomoću Electrona uključuju Visual Studio Code, Slack, Discord, Skype, Figma desktop aplikaciju, te naravno, sam GitHub Desktop. To pokazuje da je Electron sposoban pogoniti vrlo složene i široko korištene aplikacije.

Zašto koristiti Electron i TypeScript?

Prednosti Electrona:

  • Cross-platforma: Jedan codebase za sve tri glavne desktop platforme (Windows, macOS, Linux), što drastično smanjuje vrijeme i troškove razvoja.
  • Iskorištavanje web vještina: Web developeri mogu odmah početi graditi aplikacije bez učenja novih, specifičnih jezika ili frameworka.
  • Bogat ekosustav: Pristup ogromnom ekosustavu Node.js paketa (npm) za razne funkcionalnosti, od baza podataka do mrežne komunikacije.
  • Brz razvoj: Iterativni razvoj je olakšan zahvaljujući web tehnologijama i mogućnosti korištenja poznatih alata za debugiranje (npr. Chrome DevTools).
  • Fleksibilnost UI-ja: Potpuna kontrola nad izgledom i osjećajem aplikacije koristeći HTML i CSS, bez ograničenja na unaprijed definirane UI komponente.

Prednosti TypeScripta:

TypeScript je superset JavaScripta koji dodaje statičko tipiziranje. Iako Electron aplikacije mogu biti pisane i u čistom JavaScriptu, korištenje TypeScripta donosi značajne prednosti, posebno u većim projektima:

  • Rana detekcija grešaka: Tipovi pomažu uhvatiti greške tijekom razvoja, a ne tek u runtimeu.
  • Bolja čitljivost i održivost koda: Jasno definirani tipovi olakšavaju razumijevanje strukture koda i njegovo održavanje.
  • Intellisense i refactoring: IDE-ovi poput VS Codea pružaju bogatiju podršku za auto-dovršavanje, navigaciju i refactoring koda.
  • Skalabilnost: Smanjuje kompleksnost upravljanja velikim codebaseovima i timovima.
  • Moderni JavaScript: TypeScript podržava najnovije ECMAScript značajke.

Kombinacija Electrona i TypeScripta pruža snažan i robustan okvir za izradu modernih, održivih desktop aplikacija.

Arhitektura Electron Aplikacije

Electron aplikacije imaju dva glavna procesa:

  1. Main Process (Glavni Proces): Ovo je Node.js okruženje koje upravlja životnim ciklusom aplikacije, stvara prozore, upravlja sistemskim dijalozima, menijima, prečacima i povezuje se s nativnim operativnim sustavom. Ne sadrži korisničko sučelje, već kontrolira prikaz render procesa. Pristup svim Node.js API-jima i Electron modulima je moguć iz ovog procesa. Obično se pokreće iz main.ts (ili main.js) datoteke.
  2. Renderer Process (Proces Renderiranja): Svaki prozor u Electron aplikaciji je zaseban renderer process. On je zapravo mini-instanca Chromium web preglednika i zadužen je za prikaz korisničkog sučelja. Renderer proces može izvršavati standardni JavaScript, pristupati DOM-u, koristiti web API-je, ali nema izravan pristup Node.js modulima ili Electron API-jima (poput app, BrowserWindow, ipcMain). Komunikacija između glavnog i render procesa odvija se putem Inter-Process Communication (IPC) modula.

Postavljanje Razvojnog Okruženja

Za početak će nam trebati:

  • Node.js i npm (ili Yarn) instalirani
  • Tekstualni editor (preporučujem Visual Studio Code)

Inicijalizacija Projekta

Započnimo stvaranjem novog direktorija za našu aplikaciju i inicijalizacijom novog Node.js projekta:

mkdir electron-ts-app
cd electron-ts-app
npm init -y

Instalacija Electrona i TypeScripta

Sada ćemo instalirati Electron i TypeScript kao development dependencies, zajedno s ts-node (za pokretanje TypeScript koda izravno) i @types/node (za TypeScript definicije Node.js API-ja).

npm install electron@latest --save-dev
npm install typescript@latest ts-node @types/node --save-dev

Konfiguracija TypeScripta

Kreirajte tsconfig.json datoteku u korijenu projekta kako biste konfigurirali TypeScript kompajler. Možete je generirati naredbom:

npx tsc --init

Zatim, otvorite tsconfig.json i prilagodite ga. Važne postavke su:

{
  "compilerOptions": {
    "target": "ES2020",                    // Ciljana verzija JavaScripta
    "module": "CommonJS",                 // Modularni sustav
    "lib": ["ES2020", "DOM"],             // Biblioteke koje su dostupne
    "outDir": "./dist",                   // Izlazni direktorij za kompiliran JS
    "strict": true,                       // Uključi sve stroge provjere tipova
    "esModuleInterop": true,              // Omogući interop s CommonJS i ES modulima
    "skipLibCheck": true,                 // Preskoči provjeru tipova za deklaracijske datoteke
    "forceConsistentCasingInFileNames": true // Provjerava dosljednost naziva datoteka
  },
  "exclude": ["node_modules"]
}

Postavljanje Strukture Projekta

Za jednostavniju organizaciju, predlažem sljedeću strukturu:

electron-ts-app/
├── package.json
├── tsconfig.json
├── src/
│   ├── main.ts             // Glavni proces
│   ├── preload.ts          // Skripta za preloads (između main i renderer)
│   └── renderer.ts         // Glavna skripta za renderer proces
└── public/
    └── index.html          // Korisničko sučelje
    └── styles.css          // Stilovi

Kreirajte ove datoteke i direktorije.

Pisanje Koda za Glavni Proces (main.ts)

main.ts je ulazna točka vaše Electron aplikacije. On će kreirati prozore i upravljati aplikacijom.

import { app, BrowserWindow } from 'electron';
import path from 'path';

function createWindow(): void {
  // Kreiraj novi prozor preglednika.
  const mainWindow = new BrowserWindow({
    height: 600,
    width: 800,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'), // Poveži preload skriptu
      nodeIntegration: false,                      // Onemogući nodeIntegration (sigurnosni razlog)
      contextIsolation: true,                      // Izoliraj kontekste (sigurnosni razlog)
    },
  });

  // Učitaj index.html aplikacije.
  mainWindow.loadFile(path.join(__dirname, '../public/index.html'));

  // Otvori DevTools (samo za razvoj).
  // mainWindow.webContents.openDevTools();
}

// Ovaj će se dio koda izvršiti kada Electron završi inicijalizaciju
// i bude spreman za kreiranje prozora preglednika.
app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    // Na macOS-u je uobičajeno ponovno kreirati prozor
    // u aplikaciji kada se ikona docka klikne i nema drugih otvorenih prozora.
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

// Izađi iz aplikacije kada su svi prozori zatvoreni, osim na macOS-u.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

Važne sigurnosne napomene:

  • nodeIntegration: false: Onemogućuje direktan pristup Node.js API-jima iz renderer procesa. Ovo je iznimno važno za sigurnost, pogotovo ako učitavate udaljeni sadržaj ili dopuštate korisnicima da unose proizvoljan sadržaj.
  • contextIsolation: true: Osigurava da sav kod učitan u renderer procesu bude izoliran od Electron internih funkcija i preload skripti. To sprječava zlonamjeran kod da pristupi osjetljivim API-jima.
  • preload: Koristite preload skriptu za sigurno izlaganje specifičnih Node.js API-ja renderer procesu putem contextBridge.

Pisanje Koda za Predopterećenje (preload.ts)

preload.ts skripta se izvršava prije učitavanja vašeg index.html i prije bilo kojeg drugog skripta u renderer procesu. Ona ima pristup i Node.js API-jima i može manipulirati DOM-om. Ovo je idealno mjesto za izlaganje sigurnih, kontroliranih API-ja renderer procesu.

import { contextBridge, ipcRenderer } from 'electron';

// Izloži select API-je rendereru, a da ne otvori cijeli Node.js API
contextBridge.exposeInMainWorld('electronAPI', {
  // Primjer slanja poruke glavnom procesu
  sendMessage: (message: string) => ipcRenderer.send('message-from-renderer', message),

  // Primjer primanja poruke od glavnog procesa
  onReplyMessage: (callback: (event: Electron.IpcRendererEvent, message: string) => void) => {
    ipcRenderer.on('message-from-main', callback);
  },

  // Primjer pozivanja sinkronog poziva na glavni proces
  getElectronVersion: () => ipcRenderer.sendSync('get-electron-version')
});

// Ako trebate pristup DOM-u, to možete učiniti ovdje
window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector: string, text: string) => {
    const element = document.getElementById(selector);
    if (element) { element.innerText = text; }
  };

  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type as keyof NodeJS.ProcessVersions] || '');
  }
});

Pisanje Koda za Renderer Proces (renderer.ts)

renderer.ts sadrži logiku za korisničko sučelje. On nema izravan pristup Node.js API-jima, već komunicira s glavnim procesom putem API-ja koje smo izložili u preload.ts.

// Deklariraj globalni objekt koji je izložen iz preload skripte
declare global {
  interface Window {
    electronAPI: {
      sendMessage: (message: string) => void;
      onReplyMessage: (callback: (event: Electron.IpcRendererEvent, message: string) => void) => void;
      getElectronVersion: () => string;
    };
  }
}

document.addEventListener('DOMContentLoaded', () => {
  const messageInput = document.getElementById('message-input') as HTMLInputElement;
  const sendButton = document.getElementById('send-button') as HTMLButtonElement;
  const responseDiv = document.getElementById('response-div') as HTMLDivElement;
  const electronVersionSpan = document.getElementById('electron-version-info') as HTMLSpanElement;

  if (sendButton && messageInput && responseDiv && electronVersionSpan) {
    sendButton.addEventListener('click', () => {
      const message = messageInput.value;
      if (message) {
        window.electronAPI.sendMessage(message);
        messageInput.value = ''; // Očisti input
      }
    });

    window.electronAPI.onReplyMessage((_event, message) => {
      responseDiv.innerText = `Od Glavnog Procesa: ${message}`;
    });

    // Prikaz Electron verzije
    electronVersionSpan.innerText = `Electron v${window.electronAPI.getElectronVersion()}`;
  }

  // Prikaz verzija iz preload skripte
  const chromeVersion = document.getElementById('chrome-version');
  const nodeVersion = document.getElementById('node-version');
  const electronVersionPreload = document.getElementById('electron-version');

  if (chromeVersion) chromeVersion.innerText = `Chrome v${process.versions.chrome}`;
  if (nodeVersion) nodeVersion.innerText = `Node.js v${process.versions.node}`;
  // Electron verzija iz preload skripte će iskoristiti vrijednost postavljenu u globalnom 'process.versions'
  if (electronVersionPreload) electronVersionPreload.innerText = `Electron v${process.versions.electron}`;
});

HTML za Korisničko Sučelje (public/index.html)

Jednostavna HTML datoteka za naše sučelje:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Moja Electron TS Aplikacija</title>
    <link href=""styles.css">"
</head>
<body>
    <h1>Pozdrav iz Electron/TypeScript aplikacije!</h1>

    <p>Ovo je Chrome verzija: <span id="chrome-version"></span></p>
    <p>Ovo je Node.js verzija: <span id="node-version"></span></p>
    <p>Ovo je Electron verzija (iz preload-a): <span id="electron-version"></span></p>
    <p>Ovo je Electron verzija (iz renderera preko IPC-ja): <span id="electron-version-info"></span></p>

    <hr>

    <h2>IPC Test</h2>
    <input type="text" id="message-input" placeholder="Unesite poruku...">
    <button id="send-button">Pošalji poruku glavnom procesu</button>
    <div id="response-div"></div>

    <!-- Renderer skripta se uključuje ovdje -->
    <script src=""../dist/renderer.js"></script>"
</body>
</html>

CSS Stilovi (public/styles.css)

body {
    font-family: Arial, sans-serif;
    margin: 20px;
    background-color: #f0f0f0;
    color: #333;
}

h1 {
    color: #2c3e50;
}

hr {
    border: none;
    border-top: 1px solid #ccc;
    margin: 20px 0;
}

input[type="text"] {
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    width: 250px;
    margin-right: 10px;
}

button {
    padding: 8px 15px;
    background-color: #3498db;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.2s ease;
}

button:hover {
    background-color: #2980b9;
}

#response-div {
    margin-top: 15px;
    padding: 10px;
    background-color: #e8f6f3;
    border: 1px solid #d4edda;
    border-radius: 4px;
}

Komunikacija Između Procesora (IPC)

Kao što je spomenuto, glavni i renderer procesi ne dijele kontekst izravno. Komunikacija se odvija putem Electronovog ipcMain (u glavnom procesu) i ipcRenderer (u renderer procesu).

U main.ts (Glavni Proces):

Moramo dodati slušatelje za poruke koje dolaze iz renderera i poslati odgovor.

// ... (postojeći kod za createWindow i app.whenReady) ...

import { app, BrowserWindow, ipcMain } from 'electron'; // Dodajte ipcMain ovdje
import path from 'path';

// ... (ostatak koda) ...

app.whenReady().then(() => {
  createWindow();

  // Slušanje poruka od renderer procesa
  ipcMain.on('message-from-renderer', (event, message) => {
    console.log('Primio poruku od renderera:', message);
    // Pošalji odgovor natrag rendereru
    event.sender.send('message-from-main', `"Bok renderer, primio sam tvoju poruku: ${message}"`);
  });

  // Primjer sinhrone komunikacije
  ipcMain.on('get-electron-version', (event) => {
    event.returnValue = app.getVersion(); // Vraća verziju Electrona
  });

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

// ... (ostatak koda za app.on('window-all-closed')) ...

Pokretanje Aplikacije

Ažuriranje package.json

Dodajte start skriptu u package.json koja će prvo kompajlirati TypeScript, a zatim pokrenuti Electron.

{
  "name": "electron-ts-app",
  "version": "1.0.0",
  "description": "",
  "main": "dist/main.js",
  "scripts": {
    "start": "npm run build && electron .",
    "build": "tsc -p .",
    "watch": "tsc -w -p ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.12.7",
    "electron": "^29.1.5",
    "ts-node": "^10.9.2",
    "typescript": "^5.4.5"
  }
}

Sada možete pokrenuti aplikaciju:

npm start

Ovo će kompajlirati vaše TypeScript datoteke u JavaScript u dist direktorij i zatim pokrenuti Electron, učitavajući dist/main.js kao glavni proces.

Pakiranje Aplikacije

Za distribuciju vaše aplikacije krajnjim korisnicima, trebat će vam alat za pakiranje. electron-builder i electron-packager su popularni izbori. Ovdje ćemo koristiti electron-builder.

Instalacija electron-builder

npm install electron-builder --save-dev

Konfiguracija package.json za electron-builder

Dodajte build konfiguraciju i novu skriptu za pakiranje u package.json.

{
  "name": "electron-ts-app",
  "version": "1.0.0",
  "description": "",
  "main": "dist/main.js",
  "scripts": {
    "start": "npm run build && electron .",
    "build": "tsc -p .",
    "watch": "tsc -w -p .",
    "pack": "npm run build && electron-builder --dir",
    "dist": "npm run build && electron-builder"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.12.7",
    "electron": "^29.1.5",
    "electron-builder": "^24.13.3",
    "ts-node": "^10.9.2",
    "typescript": "^5.4.5"
  },
  "dependencies": {},
  "build": {
    "appId": "com.yourcompany.yourapp",
    "productName": "MojaElectronApp",
    "files": [
      "dist/**",
      "public/**",
      "package.json"
    ],
    "directories": {
      "output": "release"
    },
    "mac": {
      "category": "public.app-category.utilities"
    },
    "win": {
      "target": "nsis"
    },
    "linux": {
      "target": "AppImage"
    }
  }
}
  • appId: Jedinstveni identifikator za vašu aplikaciju.
  • productName: Naziv aplikacije koji će se prikazivati korisnicima.
  • files: Određuje koje datoteke treba uključiti u konačni paket (kompajlirani JavaScript, HTML, CSS, package.json).
  • directories.output: Direktorij u koji će se generirati izvršne datoteke.

Sada možete generirati izvršne datoteke:

npm run dist

Ovo će stvoriti instalacijske pakete (npr. .exe za Windows, .dmg za macOS, .AppImage za Linux) u release direktoriju.

Napredne Teme i Best Practices

  • State Management: Za složenije aplikacije razmislite o korištenju state management biblioteka kao što su Redux, Zustand, MobX u renderer procesu.
  • Integracija s Frameworkom: Electron se odlično integrira s modernim front-end frameworkovima poput Reacta, Vue.js-a ili Angulara. Možete koristiti Vite ili Webpack za bundleanje vašeg renderer koda i povezati ga s Electronom.
  • Ažuriranja Aplikacije: electron-updater je popularan modul za implementaciju automatskih ažuriranja za vašu Electron aplikaciju.
  • Sigurnost: Uvijek koristite contextIsolation: true i nodeIntegration: false. Pažljivo izlažite API-je putem contextBridge u preload skripti. Nikada ne učitavajte nepouzdani sadržaj s nodeIntegration omogućenim.
  • Performanse: Budući da je Electron u biti mini-preglednik, obratite pažnju na performanse i potrošnju memorije, optimizirajte vaše web resurse i koristite alate za profiliranje.

Zaključak

Electron, u kombinaciji s TypeScriptom, nudi iznimno moćan i fleksibilan način za razvoj cross-platform desktop aplikacija. Omogućuje web developerima da iskoriste svoje postojeće vještine i alate za stvaranje bogatih, nativnih iskustava. Iako postoje izazovi, poput potrošnje resursa i sigurnosnih razmatranja, pažljivim dizajnom i implementacijom, možete izgraditi impresivne aplikacije koje će služiti širokom spektru korisnika na različitim operativnim sustavima. Uz ovaj vodič, imate temelje za započinjanje vlastitog Electron/TypeScript projekta i istraživanje beskrajnih mogućnosti koje nudi ova platforma.

Sretno kodiranje!

Izvori i dodatno čitanje

  1. Electron Documentation
  2. TypeScript Handbook
  3. Electron Fiddle (za brzo prototipiranje)
  4. Electron Builder GitHub
B
Uredništvo portala

BAJT

Službeni autorski profil redakcije portala BAJT. Sadržaj priprema i provjerava uredništvo portala.