Поделитесь годовым опытом разработки Electron

внешний интерфейс Electron

Предыстория проекта

Чтобы предоставить некоторым учителям простой в использовании настольный инструмент потоковой передачи, мы планируем запустить настольное приложение Windows под названием Live Companion и быстро представить Live Companion 1.0 на Весеннем фестивале 2020 года. В настоящее время основными функциями Live Companion являются:

  • Совместим с каждым входом на сайт
  • прямая трансляция
  • Живой Лианмай
  • Интерактивный чат модератора

Разработка Live Companion ведется на основе Electron.С момента запуска проекта прошло уже больше года.Здесь я просто поделюсь опытом разработки Electron за более чем год,и в основном поделюсь некоторыми моментами на которые нужно обратить внимание при трансформации от веб-разработки до разработки на электронах.Принесите немного прибыли.

Технический отбор

Прежде чем приступить к работе с инструментами рабочего процесса push, мы изучили внутренний рабочий стол вибрато в прямом эфире — партнера по трансляции и базовую часть SDK plug-flow, выбор вибрато и прямой трансляции.Mediasdk, и, наконец, определился с техническим выбором партнера прямой трансляции Byte Live:

Графический фреймворк Фреймворк пользовательского интерфейса глобализация Упаковка приложения Пакет SDK для потоковой передачи Используйте платформу
Electron React react-intl electron-builder Mediasdk Windows

структура кода

Текущая структура каталогов Live Companion выглядит следующим образом:

  • app: Бизнес-код
    • assest: статические ресурсы, такие как изображения, svg и т. д.
    • components: деловая составляющая
    • containers: Компонент выше, чем компоненты, обычно объединяющий несколько компонентов.
    • pages: Контейнер, соответствующий каждому BrowserWindow
    • locale: языковая конфигурация, связанная с интернационализацией.
    • mainМодуль, вызываемый в основном процессе
      • lib: модули, вызываемые mediasdk, RTC и т. д. в основном процессе
      • window: Создание связанных с BrowserWindow и обработка связи процессов
    • reducers: связанный с редуксом
    • typings: определение типа ts
    • utils: Инструменты
    • package.json: pkg каталога приложения, используется для установки зависимостей, которые необходимо вызывать в основном процессе.
    • html文件: html, загруженный BrowserWindow
    • main.development.ts: Файл входа в проект, используемый для запуска всего электронного проекта.
  • builder-config: конфигурация, связанная с электронным сборщиком, включая локальную упаковку и конфигурацию онлайн-пакетов.
  • config: конфигурация, связанная с веб-пакетом
  • script: Скрипт, используемый при упаковке
  • test: шуточный тест
  • package.json: pkg корневого каталога, здесь устанавливаются интерфейсные зависимости, что позволяет избежать появления node_modules в упакованном файле.

Из структуры кода видно, что между написанием приложений Electron и веб-разработкой есть много общего:

  1. Его можно упаковать с помощью Webpack, а также есть пакеты различных компонентов React, package.json и другие файлы...
  2. В большинстве случаев пользовательский интерфейс и логика на странице могут продолжать использоваться в веб-разработке.

Подводя итог, переходя с веб-разработки на Electron, порог раннего старта низок.

Конечно, между веб-разработкой и разработкой на Electron обязательно будет разрыв, который также находится в центре внимания этого обмена.

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

Другой Официальный глоссарий Электрон

Разница между электронной разработкой и веб-разработкой

Рабочий механизм

  • Во время веб-разработки страница, которую мы разрабатываем, запускается во вкладке браузера:

Для одностраничного приложения (SPA) в качестве входного файла обычно используется HTML-файл. После упаковки интерфейсных ресурсов в HTML-код добавляется соответствующий файл bundle.js. файл загружается и соответствующий После того, как у вас есть ресурс, вы можете начать его использовать.

В то же время каждая вкладка браузера загружается в отдельный процесс.

  • При разработке Electron наша страница запускается в окне десктопного приложения:

На веб-страницах Electron вы можете вводить собственные модули Nodejs в код для использования, такие как fs, path

Здесь мы вводим окна (соответствующие ElectronBrowserWindow) приложение Electron состоит из одного или нескольких окон.В Electron каждое окно соответствует html-файлу, что похоже на веб-разработку.

Но нам все равно нужен файл входа более высокого уровня, где мы организуем логику отображения различных окон в приложении.

То есть для приложений Electron шаг «доступа через браузер» на самом деле должен выполняться разработчиком.

Представьте себе браузер, вот три вопроса, над которыми стоит подумать:

  • кто указывает, какие страницы посетить
  • Кто обрабатывает действия самого браузера, открытие и закрытие вкладок, выход из браузера
  • Кто несет ответственность за контент, отображаемый во вкладке страницы

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

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

В Electron каждое окно соответствует процессу рендеринга, и есть основной процесс, который может управлять этими процессами рендеринга, поэтому мы обычно рассматриваем основной процесс как такой менеджер.

Для приведенной выше проблемы превратите ее в концепцию Электрона и посмотрите на нее:

  • Кто указывает окно для открытия --> основной процесс
  • Кто обрабатывает логику приложения, открытие и закрытие окон, выход из приложения -> основной процесс
  • Кто отвечает за отображаемый в окне контент ---> процесс рендеринга

Различные окна взаимодействуют с основным процессом, и главный процесс выдает команды для формирования полного приложения Electron:

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

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

image.png

Основной процесс и процесс рендеринга

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

Жизненный цикл электрона

Проекты, которые транслируются в прямом эфире в байтах, перечислены в структуре кода.main.development.tsТакой файл, это входной файл проекта, в этом файле мы имеем дело с некоторымиBrowserWindowинициализация, регистрация необходимых обработчиков событий в основном процессе, отслеживание отчетов по данным и т. д. Самое главное, что мы имеем дело с жизненным циклом приложения Electron.готовое событие.

image.png

image.png

Жизненный цикл электрона

  • запуск приложения

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

существуетmain.development.tsМы в приложенииreadyКогда событие сработает, создайте страницу входа, страницу потока и т. д.BrowserWindow, а в окне входаready-to-showКогда событие срабатывает,showспособ отображения окна входа в Live Companion для пользователя.

  • Выход из приложения

Всего на данный момент 6 живых партнеровBrowserWindow, когда нам нужно выйти из приложения, мы уничтожим все текущиеBrowserWindowэкземпляр, который вызоветwindow-all-closeсобытие, здесь черезelectron.app.quitметод, полностью закройте приложение.

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

function 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() 
  } 
}) 

Пример официального файла записи Electron

Опыт оптимизации

Предварительно созданный BrowserWindow

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

BrowserWindow похож на вкладку браузера Chrome, в приложениях Electron каждое окно соответствуетBrowserWindowэкземпляр, который имеет свой собственный процесс рендеринга.

При создании мы можем указать размер, цвет, видимость и другие свойства этого окна.Документация BrowserWindow, когда мы создаем окно, передаемloadURLМетод загружает указанный html для загрузки нашей страницы интерфейса.

В идеале поток для открытия нового окна будетnew BrowserWindow->load js->执行初始化逻辑, закрытие окна пропускает этот экземпляр черезcloseметод уничтожить, а на самом деле каждый раз заново создаватьBrowserWindowНакладные расходы относительно велики, и время ожидания пользователя часто невыносимо от создания до полностью интерактивной страницы.

В практических ситуациях мы обычно заранее создаем часто используемый BrowserWindow, а когда нам нужно его использовать, мы напрямую передаемshowОтображение метода, когда вы нажимаете, чтобы закрыть, пройтиhideМетод скрывает его в фоновом режиме, чтобы его можно было сразу открыть в следующий раз, сокращая время ожидания пользователя.Вот простая стратегия обмена пространством на время.Есть также много вопросов для обсуждения интерактивной оптимизации Электрона и того, как поймать с родным интерактивным опытом.

Когда мы показываем использование интерфейса Electronshow-hideрежим заменяетcreate-close, вот некоторые моменты, которые отличаются от разработки веб-страницы:

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

Здесь мы конструируем сцену.После входа в систему мы попадаем в окно списка прямых трансляций.После нажатия на комнату прямых трансляций будет отображена стартовая страница комнаты прямых трансляций.

image.png

image.pngВзяв за пример инициализацию front-end страницы, когда мы переходим к инициализации страницы запуска, если она разработана по типу хуков, мы обычно указываем пустой массив в useEffect, что может обеспечить инициализацию всего компонента один раз, когда он загружается, например, из избыточности, Получите данные, запросите интерфейс и обновите соответствующее представление.

  useEffect(() => {
    init()
  }, [])

Инициализация страниц в BrowerWindow

И мы обычно создаем заранее, чтобы оптимизировать интерактивный опыт пользователя.BrowserWindow, и после этого сработает логика инициализации, что эквивалентно выполнению логики инициализации до клика, чего не ожидается.

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

Есть два пути решения этого случая:

  • В useEffect отслеживается переменная, и при изменении значения выполняется логика инициализации
  • Посредством связи процесса основной процесс отправляет событие инициализации процессу рендеринга стартовой страницы.

Если мы используем первый метод мониторинга переменных, наш код может быть преобразован в:

  useEffect(() => {
    init(activityId)
  }, [activityId])

Таким образом, когда мы нажимаем на живую комнату на странице списка, мы обновляем значение activityId, хранящееся в избыточности, и страница запуска обнаруживает изменение activityId, что может гарантировать, что логика инициализации выполняется правильно каждый раз, когда ActivityId переключился.

Существуют также недостатки использования этого метода, который эквивалентен использованию значения activityId в качестве триггерной основы для страницы для выполнения логики инициализации, а это означает, что нам необходимо уточнить, где это значение может вызвать логику изменения, чтобы предотвратить выполнение. логика инициализации, которая не соответствует ожиданиям.

Если мы воспользуемся вторым методом для выполнения инициализации через обмен данными между процессами, модификация кода будет немного сложнее.

Во-первых, нам нужно добавить прослушиватель на страницу внешнего интерфейса, обычно черезipcRenderer.onреализация метода,ipcRendererСобытия, отправленные основным процессом, могут быть получены.Наш метод обработки здесь:

  1. После щелчка в окне списка перейдитеipcRenderer.sendМетод отправляет это событие щелчка в основной процесс и переноситactivityIdпараметр
  2. Основной процесс через окно трансляцииBrowserWindowпримерwebContents.sendспособ отправкиSHOW_MAIN_WINDOWсобытие, проходactivityIdпараметр
  3. Процесс рендеринга окна запуска проходит черезipcRenderer.onмониторSHOW_MAIN_WINDOWсобытие, прочитайте идентификатор активности и выполните процесс инициализации страницы

image.png

Таким образом, код на странице запуска может быть преобразован в:

  const handleInit = (data)=>{
      const { activityId } = data
      init(activityId)
  }  

  useEffect(() => {
    ipcRenderer.on(EVENT.SHOW_MAIN_WINDOW, handleInit)
    return ()=>{
        ipcRenderer.removeListener(EVENT.SHOW_MAIN_WINDOW, handleInit)
    }
  }, [])

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

Конечно, если мы используем коммуникацию процесса, нам также нужно обратить внимание при использовании слушателя, он не может быть прочитан в хуке.useStateа такжеreduxПоследнее значение в , если нашей логике инициализации нужны какие-то дополнительные параметры на странице, рекомендуется использоватьuseRefсохранить эту переменную вinitпройти, когдаref.currentчитать.

Суммировать

Когда мы разрабатываем проекты Electron, уровень пользовательского интерфейса может в основном повторно использовать опыт веб-разработки,BrowserWindowПо существу также загружается html.

Подводя итог, в Electron нам нужно разработать:

  • страница в Интернете
  • Управление отдельными окнами
  • Связанные с приложением: панель меню, жизненный цикл (открытие и закрытие приложений и т. д.)