Копирайте данни от клипборда между приложенията на React в няколко прозореца.

Множеството прозорци в Electron не споделят един и същ модел на данни и трябва да използват междупроцесна комуникация, за да споделят данни.

Въпреки че всички прозорци идват от едно и също приложение, всеки прозорец е екземпляр на BrowserWindow, съдържащ уникален уеб изглед на Chromium. Изпращането на съобщения между прозорци е подобно на раздели или прозорци в уеб браузър.

В уеб браузър можете да комуникирате чрез BroadcastChannels.

След като дефинирате канал, публикуваните съобщения изпращат събития за слушателите:

const channel = new BroadcastChannel("mychannel");
// Send a message:
channel.postMessage("Hello, world");
// Receive a message:
channel.onmessage = (event) => {
  console.log(event);
});

В Electron подобна стратегия се използва чрез изпращане на съобщения от процеса на изобразяване към основния процес. След това основният процес изпраща съобщения до всички прозорци за рендиране.

Пример за клипборд

Например, помислете за разширени данни от клипборда, които искаме да споделяме между прозорците. Всеки прозорец може да изпълнява уникални приложения; или множество копия на едно и също приложение.

Ще продължа от моята публикация в приложението Multiple Window Electron:



В този пример нови прозорци могат да се отварят чрез Файл ≫ Нов прозорец, което ефективно създава нов екземпляр на точно същото приложение React.

Изпращане на данни

От уеб съдържанието на който и да е прозорец сериализирайте полезни данни в JSON и ги изпратете през ipcRenderer до желан канал. В този пример клиентът ще изпрати данните от клипборда, като използва clipboard-send.

const { ipcRenderer } = require('electron');
const copy = (data) => {
  const json = JSON.stringify(data);
  ipcRenderer.send("clipboard-send", json);
}

В главната нишка (main.dev.js) създайте слушател на ipcMain, за да обработвате clipboard-send канални събития, които са били изпратени от процеса на изобразяване на всеки прозорец. След това предайте тези данни обратно на всички прозорци, като изпратите clipboard-receive канални събития:

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

ipcMain.on('clipboard-send', (event, json) => {
  windows.forEach(window => {
    window.send('clipboard-receive', json);
  });
});

Получаване на данни

Сега с основната нишка, изпращаща съобщението до всички прозорци, всеки прозорец трябва да слуша за събития в канала clipboard-receive:

const { ipcRenderer } = require('electron');
let clipboard = {};
ipcRenderer.on('clipboard-receive', (event, json) => {
  if (!json) return;
  const data = JSON.parse(json);
  clipboard = data;
});

Нишката за изобразяване на всеки прозорец вече може да десериализира полезния товар от данни и да го кешира локално в клипборда.

Пълен жизнен цикъл:

  • Нишката за изобразяване на един прозорец изпраща съобщение до основната нишка
  • Основната нишка повтаря съобщението до всички прозорци
  • Всички прозорци получават събитието с полезни данни

Нов Windows

Тук има прозорци, създадени след излъчване на данни. Продължавайки с примера с клипборда, да кажем:

  • Един прозорец копира данни в клипборда
  • Беше отворен нов прозорец
  • Потребителят се опитва да постави в новия прозорец - този прозорец не е получил никакво събитие, тъй като е бил създаден след излъчването на съобщението.

В този случай можете да създадете локален кеш в главния рендър; след това автоматично изпраща данните от клипборда, когато се създават нови прозорци.

const { ipcMain } = require('electron');
let clipboardCache;
export const createWindow = async () => {
  let newWindow = new BrowserWindow();
  // ...
  newWindow.webContents.on('did-finish-load', () => {
    // Send clipboard data
    newWindow.send('clipboard-receive', clipboardCache);
  });
}

Събираме всичко заедно

В рамките на кода на JavaScript, показан във вашите прозорци, като например приложение на React, изпращайте и получавайте събития към основната нишка. Това наподобява комуникация между клиент и сървър:

const { ipcRenderer } = require('electron');
// Local clipboard cache
let clipboard = {};
// Send clipboard data to the main thread
const copy = (data) => {
  const json = JSON.stringify(data);
  ipcRenderer.send("clipboard-send", json);
};
// Listen for incoming clipboard data
ipcRenderer.on('clipboard-receive', (event, json) => {
  if (!json) return;
  
  const data = JSON.parse(json);
  clipboard = data;
});
// Do something with the clipboard data
const paste = () => {
  if (!clipboard) return;
  // ...
};

В основната нишка (main.dev.js), добавяйки към предишния множествен пример:

const { ipcMain } = require('electron');
// Clipboard cache
let clipboard;
const windows = new Set();
export const createWindow = async () => {
  if (
    process.env.NODE_ENV === 'development' ||
    process.env.DEBUG_PROD === 'true'
  ) {
    await installExtensions();
  }
  let x, y;
  const currentWindow = BrowserWindow.getFocusedWindow();
  if (currentWindow) {
    const [currentWindowX, currentWindowY] = currentWindow.getPosition();
    x = currentWindowX + 24;
    y = currentWindowY + 24;
  }
  let newWindow = new BrowserWindow({
    show: false,
    width: 1200,
    height: 812,
    x,
    y,
    webPreferences: {
      nodeIntegration: true
    }
  });
  newWindow.loadURL(`file://${__dirname}/app.html`);
  newWindow.webContents.on('did-finish-load', () => {
    if (!newWindow) {
      throw new Error('"newWindow" is not defined');
    }
    if (process.env.START_MINIMIZED) {
      newWindow.minimize();
    } else {
      newWindow.show();
      newWindow.focus();
    }
    // Send clipboard data to new window
    newWindow.send('clipboard-receive', clipboard);
  });
  newWindow.on('closed', () => {
    windows.delete(newWindow);
    newWindow = null;
  });
  newWindow.on('focus', () => {
    const menuBuilder = new MenuBuilder(newWindow);
    menuBuilder.buildMenu();
  });
  windows.add(newWindow);
  return newWindow;
};
app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (windows.size === 0) createWindow();
});

// Echo clipboard data back to all windows:
ipcMain.on('clipboard-send', (event, json) => {
  clipboard = json;
  windows.forEach(window => {
    window.send('clipboard-receive', json);
  });
});