2.0.0 • Published 4 months ago

emr-bridge v2.0.0

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Electron main-renderer bridge (emr-bridge)

Только для Electron.

Полная поддержка TypeScript.

Уменьшает boilerplate при работе с Electron IPC:

  • Функционал событий.
  • Лёгкая передача экземпляров классов.
  • Простое использование промисов.
  • Быстрая публикация переменных и функций из main в renderer / preload.

Содержание

Установка

npm i emr-bridge@latest

Для работы библиотеки требуется вставить следующий код в preload процесс.

import { provideFromMain } from 'emr-bridge/preload'

provideFromMain(true /* context isolation */)

Использование

Если требуется CommonJS, то используем emr-bridge/cjs.

Публикация функций и переменных

Для публикации функций и переменных из main процесса библиотека предоставляет функции publishFunction и publishVariable из emr-bridge/main.

// Main process
import { publishFunction, publishVariable } from 'emr-bridge/main'

publishFunction('sayHello', name => {
  return `Hello, ${name}!`
})

publishFunction('getUser', () => {
  return {
    name: 'Name',
    age: 20
  }
})

let count = 0

publishVariable('count', {
  get: () => count,
  set: value => count = value
})

Использование в renderer и preload

Пример использования ранее опубликованных функций и переменных.

// Renderer process
import { Bridge } from 'emr-bridge/renderer'

// `as` в TypeScript позволяет кастовать в нужный тип.
const bridge = Bridge.as()

// Выведет текущее значение count из main (0).
console.log(bridge.count)

// Увеличит значение count в main на единицу.
bridge.count++

// { name: 'Name', age: 20 }.
bridge.getUser()

// 'Hello, Aleksandr!'.
bridge.seyHello('Aleksandr')

Для preload процесса всё идентично, но требуется импортировать из emr-bridge/preload.

Промисы

Поддержка промисов реализована "из коробки".

// Main process
import { publishFunction } from 'emr-bridge/main'

publishFunction('after1s', () => {
  return new Promise(resolve => {
    setTimeout(() => resolve('after1s'), 1000)
  })
})
// Renderer process
import { Bridge } from 'emr-bridge/renderer'

const bridge = Bridge.as()

async function foo() {
  const result = await bridge.after1s()

  // Через 1 секунду выведет сообщение 'after1s', полученное из main.
  console.log(result)
}

События

Пример обработки события, которое вызывается из renderer или preload.

// Main process
import { on, once } from 'emr-bridge/main'

on('message-from-renderer', message => console.log(message))
// once('message-from-renderer', message => console.log(message))
// Renderer process
import { Bridge } from 'emr-bridge/renderer'

const bridge = Bridge.as()

// Вызовет событие и выведет в консоль main процесса 'Hello from renderer'
bridge.emit('message-from-renderer', 'Hello from renderer')

Пример обработки события, которое вызывается из main.

// Main process
import { emitEvent } from 'emr-bridge/main'

// Вызовет событие и выведет в консоль renderer процесса 'Hello from main'
emitEvent('message-from-main', 'Hello from main')
// Renderer process
import { Bridge } from 'emr-bridge/renderer'

const bridge = Bridge.as()

bridge.on('message-from-main', message => console.log(message))

Передача экземпляров классов

По умолчанию, передача экземпляров пользовательских классов через IPC невозможна.

Библиотека решает эту проблему путём реализации паттерна 'Снимок'.

Передаваемый класс должен реализовать функции getSnapshot и updateFromSnapshot, а также иметь конструктор без параметров.

// Human.js
export default class Human {
  name = undefined
  age = undefined

  constructor(
    name = 'Aleksandr',
    age = 30
  ) {
    this.name = name
    this.age = age
  }

  getSnapshot() {
    return {
      name: this.name,
      age: this.age
    }
  }

  updateFromSnapshot(snapshot) {
    this.name = snapshot.name
    this.age = snapshot.age
  }
}

Для передачи экземпляра класса требуется на принимающей стороне передать сам класс в качестве аргумента функции:

// Main process
import { publishFunction } from 'emr-bridge/main'
import Human from './Human'

// Выводим имя человека, переданного из `preload` или `renderer`.
publishFunction(
  'displayName',
  human => console.log(human.name),
  /* Говорим какие классы надо принять.
     Порядок в массиве соответствует порядку таких аргументов в функции.
     Для (human, car, engine) будет [Human, Car, Engine].
     Для (human, surname, car) будет [Human, Car], так как 'surname' имеет стандартный тип String.
     Стандартные типы (String, Number..) указывать не требуется. */
  [Human]
)

/* У событий также есть такой аргумент
  on('displayName', human => console.log(human.name), [Human])
  once('displayName', human => console.log(human.name), [Human])
*/
// Renderer process
import { Bridge } from 'emr-bridge/renderer'
import Human from './Human'

const bridge = Bridge.as()

// Передаст данные через создание снимка.
bridge.displayName(new Human('Dima', 25))

Для получения экземпляра класса из main в renderer или preload нужно использовать bridge.call.

Такое требование связано со сложностью реализации.

// Main process
import { publishFunction } from 'emr-bridge/main'
import Human from './Human'

// Передаём человека в `preload` или `renderer` процесс.
publishFunction('getHuman', () => new Human('Artem', 40))
// Renderer process
import { Bridge } from 'emr-bridge/renderer'
import Human from './Human'

const bridge = Bridge.as()

// Выведет в консоль 25
console.log(
  bridge.call(bridge.getHuman, Human).name
)

/* Альтернативный вариант
// Оборачиваем оригинал новой функцией, которая автоматически преобразует ответ в нужный класс.
bridge.getHuman = bridge.returns(bridge.getHuman, Human)

console.log(bridge.getHuman().name)
*/
2.0.0

4 months ago

1.7.1

9 months ago

1.7.0

10 months ago

1.6.2

11 months ago

1.6.1

11 months ago

1.6.0

11 months ago

1.5.0

1 year ago

1.4.1

1 year ago

1.4.0

1 year ago

1.2.2

1 year ago

1.3.0

1 year ago

1.2.1

1 year ago

1.2.0

2 years ago

1.1.1

2 years ago

1.1.2

2 years ago

1.1.0

2 years ago

1.0.0

2 years ago