Spec-Zone.ru › Electron

Использование скриптов предварительной загрузки

Следуйте по уроку

Это часть 3 учебника по Electron.

  1. Предварительные условия
  2. Создание вашего первого приложения
  3. Использование скриптов предварительной загрузки
  4. Добавление функций
  5. Упаковка вашего приложения
  6. Публикация и обновление

Цели обучения​

В этой части учебника вы узнаете, что такое скрипт предварительной загрузки и как его использовать для безопасного предоставления привилегированных API в процессе рендеринга. Вы также узнаете, как общаться между процессами «главным» и «рендеринга» с помощью модулей межпроцессного взаимодействия (IPC) Electron.

Что такое скрипт предварительной загрузки?​

Главный процесс Electron — это среда Node.js, имеющая полный доступ к операционной системе. Помимо модулей Electron, вы также можете получить доступ к встроенным функциям Node.js, а также к любым пакетам, установленным через npm. С другой стороны, процессы рендеринга запускают веб-страницы и по умолчанию не запускают Node.js по соображениям безопасности.

Для объединения различных типов процессов Electron нам понадобится специальный скрипт, называемый скриптом предварительной загрузки.

Расширение рендера с помощью скрипта предварительной загрузки​

Скрипт предварительной загрузки BrowserWindow выполняется в контексте, имеющем доступ как к DOM HTML, так и к среде Node.js. Скрипты предварительной загрузки вставляются перед загрузкой веб-страницы в процесс рендеринга, аналогично скриптам контента расширения Chrome.

Чтобы добавить в процесс рендеринга функции, требующие привилегированного доступа, вы можете определить глобальные объекты с помощью API contextBridge.

Для демонстрации этого понятия вы создадите скрипт предварительной загрузки, который предоставит версии вашего приложения для Chrome, Node и Electron в процессе рендеринга.

Добавьте новый preload.js скрипт, который предоставляет выбранные свойства объекта Electron в process.versions глобальной переменной в процессе рендеринга.

preload.js
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  // we can also expose variables, not just functions
})

Чтобы прикрепить этот скрипт к вашему процессу рендеринга, передайте его путь в webPreferences.preload параметр в конструкторе BrowserWindow:

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

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })

  win.loadFile('index.html')
}

app.whenReady().then(() => {
  createWindow()
})
информация

Здесь используются два понятия Node.js:

  • Строка __dirname указывает на путь к выполняемому скрипту (в данном случае — корневая папка вашего проекта).
  • API path.join объединяет несколько сегментов пути, создавая комбинированный путь, который работает на всех платформах.

В этот момент процесс рендеринга имеет доступ к глобальной переменной versions, поэтому давайте отобразим эту информацию в окне. К этой переменной можно получить доступ через window.versions или просто versions.

Создайте renderer.js скрипт, использующий API DOM document.getElementById, чтобы заменить отображаемый текст HTML-элемента с info в качестве его свойства id.

renderer.js
const information = document.getElementById('info')
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`

Затем измените index.html файл, добавив новый элемент с info в качестве свойства id, и прикрепите ваш renderer.js скрипт:

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Hello from Electron renderer!</title>
  </head>
  <body>
    <h1>Hello from Electron renderer!</h1>
    <p>👋</p>
    <p id="info"></p>
  </body>
  <script src="./renderer.js"></script>
</html>

После выполнения вышеперечисленных шагов ваше приложение должно выглядеть примерно так:

Electron app showing This app is using Chrome (v102.0.5005.63), Node.js (v16.14.2), and Electron (v19.0.3)

И код должен выглядеть так:

docs/fiddles/tutorial-preload (20.1.0)Открыть в Fiddle
  • main.js
  • preload.js
  • index.html
  • renderer.js
const { app, BrowserWindow } = require('electron');
const path = require('path');

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });

  win.loadFile('index.html');
};

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

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

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

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
});
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <meta
      http-equiv="X-Content-Security-Policy"
      content="default-src 'self'; script-src 'self'"
    />
    <title>Hello from Electron renderer!</title>
  </head>
  <body>
    <h1>Hello from Electron renderer!</h1>
    <p>👋</p>
    <p id="info"></p>
  </body>
  <script src="./renderer.js"></script>
</html>
const information = document.getElementById('info');
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`;

Обмен данными между процессами​

Как уже упоминалось выше, главный и рендеринг процессы Electron имеют разные обязанности и не взаимозаменяемы. Это означает, что невозможно напрямую получить доступ к API Node.js из процесса рендеринга, а также к DOM HTML из главного процесса.

Решение этой проблемы — использование модулей Electron ipcMain и ipcRenderer для межпроцессного взаимодействия (IPC). Чтобы отправить сообщение со своей веб-страницы в главный процесс, вы можете настроить обработчик главного процесса с помощью ipcMain.handle и затем экспонировать функцию, которая вызывает ipcRenderer.invoke, чтобы запустить обработчик в скрипте предварительной загрузки.

Для иллюстрации мы добавим в процесс рендеринга глобальную функцию ping(), которая вернёт строку из главного процесса.

Сначала настройте вызов invoke в скрипте предварительной загрузки:

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

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke('ping'),
  // we can also expose variables, not just functions
})
Безопасность IPC

Обратите внимание, как мы обернули вызов ipcRenderer.invoke('ping') в вспомогательную функцию, а не экспонировали непосредственно модуль ipcRenderer. Вы никогда не должны напрямую экспонировать весь модуль ipcRenderer через предварительную загрузку. Это даст вашему процессу рендеринга возможность отправлять произвольные сообщения IPC в главный процесс, что становится мощным вектором атаки для вредоносного кода.

Затем настройте обработчика handle в главном процессе. Мы делаем это до загрузки HTML-файла, чтобы обработчик был готов перед отправкой invoke вызова из процесса рендеринга.

main.js
const { ipcMain } = require('electron')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  })
  ipcMain.handle('ping', () => 'pong')
  win.loadFile('index.html')
}

После настройки отправителя и получателя вы можете теперь отправлять сообщения из процесса рендеринга в главный процесс через канал 'ping' , который вы только что определили.

renderer.js
const func = async () => {
  const response = await window.versions.ping()
  console.log(response) // prints out 'pong'
}

func()
информация

Для более подробного объяснения использования модулей ipcRenderer и ipcMain, ознакомьтесь с полным руководством по межпроцессного взаимодействия.

Заключение​

Скрипт предварительной загрузки содержит код, который выполняется перед загрузкой вашей веб-страницы в окно браузера. Он имеет доступ как к API DOM, так и к среде Node.js и часто используется для предоставления привилегированных API для процесса рендеринга через API contextBridge.

Поскольку основные и рендерные процессы имеют совершенно разные обязанности, приложения Electron часто используют скрипт предварительной загрузки для настройки интерфейсов межпроцессного взаимодействия (IPC) для передачи произвольных сообщений между этими двумя типами процессов.

В следующей части учебника мы покажем вам ресурсы по добавлению дополнительной функциональности в ваше приложение, а затем научим вас распространять ваше приложение среди пользователей.

© GitHub Inc.
Licensed under the MIT license.
https://www.electronjs.org/docs/latest/tutorial/tutorial-preload

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API