İçeriğe geç

Usage with Electron

Bu içerik henüz dilinizde mevcut değil.

Electron applications have a special architecture consisting of multiple processes with different responsibilities. Applying FSD in such a context requires adapting the structure to the Electron specifics.

  • Dizinsrc
    • Dizinapp Common app layer
      • Dizinmain Main process
        • index.ts Main process entry point
      • Dizinpreload Preload script and Context Bridge
        • index.ts Preload entry point
      • Dizinrenderer Renderer process
        • index.html Renderer process entry point
    • Dizinmain
      • Dizinfeatures
        • Dizinuser
          • Dizinipc
            • get-user.ts
            • send-user.ts
      • entities
      • shared
    • Dizinrenderer
      • Dizinpages
        • Dizinsettings
          • Dizinipc
            • get-user.ts
            • save-user.ts
          • Dizinui
            • user.tsx
          • index.ts
        • Dizinhome
          • Dizinui
            • home.tsx
          • index.ts
      • widgets
      • features
      • entities
      • shared
    • Dizinshared Common code between main and renderer
      • ipc IPC description (event names, contracts)

Each process must have its own public API. For example, you can’t import modules from main to renderer. Only the src/shared folder is public for both processes. It’s also necessary for describing contracts for process interaction.

Additional changes to the standard structure

Section titled “Additional changes to the standard structure”

It’s suggested to use a new ipc segment, where interaction between processes takes place. The pages and widgets layers, based on their names, should not be present in src/main. You can use features, entities and shared. The app layer in src contains entry points for main and renderer, as well as the IPC. It’s not desirable for segments in the app layer to have intersection points

src/shared/ipc/channels.ts
export const CHANNELS = {
GET_USER_DATA: 'GET_USER_DATA',
SAVE_USER: 'SAVE_USER',
} as const;
export type TChannelKeys = keyof typeof CHANNELS;
src/shared/ipc/events.ts
import { CHANNELS } from './channels';
export interface IEvents {
[CHANNELS.GET_USER_DATA]: {
args: void,
response?: { name: string; email: string; };
};
[CHANNELS.SAVE_USER]: {
args: { name: string; };
response: void;
};
}
src/shared/ipc/preload.ts
import { CHANNELS } from './channels';
import type { IEvents } from './events';
type TOptionalArgs<T> = T extends void ? [] : [args: T];
export type TElectronAPI = {
[K in keyof typeof CHANNELS]: (...args: TOptionalArgs<IEvents[typeof CHANNELS[K]]['args']>) => IEvents[typeof CHANNELS[K]]['response'];
};
src/app/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron';
import { CHANNELS, type TElectronAPI } from 'shared/ipc';
const API: TElectronAPI = {
[CHANNELS.GET_USER_DATA]: () => ipcRenderer.sendSync(CHANNELS.GET_USER_DATA),
[CHANNELS.SAVE_USER]: args => ipcRenderer.invoke(CHANNELS.SAVE_USER, args),
} as const;
contextBridge.exposeInMainWorld('electron', API);
src/main/features/user/ipc/send-user.ts
import { ipcMain } from 'electron';
import { CHANNELS } from 'shared/ipc';
export const sendUser = () => {
ipcMain.on(CHANNELS.GET_USER_DATA, ev => {
ev.returnValue = {
name: 'John Doe',
email: 'john.doe@example.com',
};
});
};
src/renderer/pages/user-settings/ipc/get-user.ts
import { CHANNELS } from 'shared/ipc';
export const getUser = () => {
const user = window.electron[CHANNELS.GET_USER_DATA]();
return user ?? { name: 'John Donte', email: 'john.donte@example.com' };
};