Управляемое чтение
использоватьElectron
Клиентская программа разрабатывалась давно, общее впечатление пока очень хорошее.Есть и подводные камни.Статья от [принцип работы] до [практическое применение].Electron
Сделайте систематический вывод. [Несколько картинок, предупреждение о длинном тексте~]
Все коды примеров в этой статье находятся в моемgithub electron-reactВыше статью лучше читать в сочетании с кодом. Кроме тогоelectron-react
также может использоваться какElectron + React + Mobx + Webpack
Разработка строительных лесов для стека технологий.
1. Настольные приложения
Настольные приложения, также известные как программы с графическим интерфейсом пользователя (Graphical User Interface), но есть некоторые отличия от программ с графическим интерфейсом. Настольное приложение Преобразование программ с графическим интерфейсом пользователя из графического пользовательского интерфейса в «рабочий стол» делает холодную, подобную дереву компьютерную концепцию более гуманной, яркой и динамичной.
Различные клиентские программы, используемые на наших компьютерах, являются настольными приложениями, и в последние годыWEB
И рост мобильных терминалов постепенно затмил настольные приложения, но настольные приложения по-прежнему важны для некоторых повседневных функций или отраслевых приложений.
Традиционными методами разработки настольных приложений обычно являются следующие два:
1.1 Нативная разработка
Скомпилируйте язык прямо в исполняемый файл и вызовите систему напрямуюAPI
, полный рисунок пользовательского интерфейса и т. д. Этот тип технологии разработки имеет высокую эффективность работы, но, как правило, скорость разработки ниже, а технические требования выше, например:
- использовать
C++ / MFC
развиватьWindows
применение - использовать
Objective-C
развиватьMAC
применение
1.2 Платформа хостинга
Вначале была локальная разработка и разработка пользовательского интерфейса. После одной компиляции получается промежуточный файл, а операция компиляции или интерпретации вторичной загрузки выполняется через платформу или виртуальную машину. Операционная эффективность ниже, чем у нативной компиляции, но после оптимизации платформы ее эффективность также значительна. С точки зрения скорости разработки, она быстрее собственной технологии компиляции. Например:
- использовать
C# / .NET Framework
(только развиватьWindows应用
) Java / Swing
Тем не менее, два вышеупомянутых слишком недружелюбны к фронтенд-разработчикам, и в основном это области, в которые фронтенд-разработчики не вовлекаются.WEB
То, как технология развивалась, клиент появился из ниоткуда.
1.3 Веб-разработка
использоватьWEB
технологии для разработки, используя движок браузера для завершенияUI
рендерить, использоватьNode.js
Реализовать серверную частьJS
Запрограммируйте и вызовите системуAPI
, вы можете думать об этом как о клиентской оболочкеWEB
применение.
На интерфейсе,WEB
Мощная экосистемаUI
Это дает бесконечные возможности, а затраты на разработку и обслуживание относительно низки.WEB
Фронтенд-разработчики с опытом разработки могут легко начать разработку.
В этой статье речь пойдет об использованииWEB
Одна из технологий разработки клиентских программ [electron
】
2. Электрон
Electron
ОтGithub
развиваться сHTML,CSS
иJavaScript
Библиотека с открытым исходным кодом для создания кроссплатформенных настольных приложений.Electron
поставивChromium
иNode.js
в ту же среду выполнения и упаковать его какMac,Windows
иLinux
Приложение в системе достигает этой цели.
2.1 Причины для разработки с Electron:
- Используйте сильный экологический
Web
Развитие технологий, низкая стоимость разработки, сильная масштабируемость, более крутойUI
- Кроссплатформенный, набор кода может быть упакован как
Windows、Linux、Mac
Три комплекта софта, и компиляция быстрая - непосредственно на существующем
Web
Расширьте приложение, чтобы предоставить возможности, которых нет в браузерах. - Ты фронтенд 👨💻~
Конечно, надо признать и его недостатки: производительность ниже, чем у нативного десктопного приложения, а конечное упакованное приложение намного больше нативного приложения.
2.2 Опыт разработки
совместимость
Хотя вы все еще используетеWEB
технология для разработки, но вам больше не нужно учитывать вопросы совместимости, вам нужно только заботиться о своем текущем использованииElectron
версия, соответствующаяChrome
версия, которая, как правило, является достаточно новой, чтобы вы могли использовать последнююAPI
и синтаксис, вы также можете вручную обновитьChrome
Версия. Точно так же вам не нужно беспокоиться о проблемах стиля и совместимости кода с различными браузерами.
Среда узла
Это может быть функция, о которой мечтали многие фронтенд-разработчики.WEB
используется в интерфейсеNode.js
МощныйAPI
, а это значит, что выWEB
Страница может напрямую управлять файлом и вызывать системуAPI
и даже манипулировать базой данных. Конечно, помимо полногоNode API
, вы также можете использовать дополнительные сотни тысячnpm
модуль.
перекрестный домен
вы можете напрямую использоватьNode
который предоставилrequest
Модули выполняют сетевые запросы, а это значит, что вам больше не нужно беспокоиться о междоменном взаимодействии.
мощная расширяемость
с помощьюnode-ffi
, обеспечивающий мощную расширяемость приложений (подробнее об этом в последующих главах).
2.3 Кто использует Электрон
Сейчас на рынке много приложенийElectron
развитые, в том числе и наши знакомыеVS Code
клиент,GitHub
клиент,Atom
клиент и так далее. Впечатленный, в прошлом году Xunlei выпустил Xunlei X10.1
Когда текст:
Начиная с версии Xunlei X 10.1, мы полностью переписали основной интерфейс Xunlei с использованием программной среды Electron. Thunder X, использующий новый фреймворк, может отлично поддерживать дисплеи высокой четкости, такие как 2K и 4K, а рендеринг текста в интерфейсе также более четкий и четкий. С технической точки зрения отрисовка интерфейса и обработка событий нового фреймворка более гибкие и эффективные, чем в старом фреймворке, поэтому беглость интерфейса также значительно лучше, чем у Thunderbolt старого фреймворка. Что касается конкретного улучшения? Вы узнаете, когда попробуете.
ты можешь открытьVS Code
, нажмите [Справка] [Переключить инструменты разработчика] для отладкиVS Code
клиентский интерфейс.
3. Принцип работы электрона
Electron
КомбинированныйChromium
,Node.js
и для вызова собственных функций ОСAPI
.
3.1 Chromium
Chromium
даGoogle
для развитияChrome
Проект с открытым исходным кодом, запущенный браузером,Chromium
эквивалентноChrome
Инженерная версия или экспериментальная версия, новые функции будут первыми вChromium
Он будет реализован наChrome
вверх, такChrome
Функция будет относительно отсталой, но более стабильной.
Chromium
заElectron
обеспечить мощныйUI
Возможность разработки интерфейсов без оглядки на совместимость.
3.2 Node.js
Node.js
это пустьJavaScript
Платформа разработки, работающая на стороне сервера,Node
Используйте управляемые событиями, неблокирующиеI/O
модель должна быть легкой и эффективной.
в одиночествеChromium
не способен к прямому манипулированию роднымGUI
способный,Electron
интегрированы вNodejs
, что позволяет ему иметь нижний слой операционной системы при разработке интерфейсаAPI
Способность,Nodejs
обычно используется вPath、fs、Crypto
ждем модуль вElectron
Можно использовать напрямую.
3.3 Системный API
Для того, чтобы обеспечить родную системуGUI
служба поддержки,Electron
Встроенный собственный интерфейс прикладной программы обеспечивает поддержку некоторых системных функций, таких как вызов системных уведомлений и открытие системных папок.
В режиме разработки,Electron
вызов системыAPI
Он разработан отдельно от интерфейса рисования, давайте посмотрим.Electron
О том, как разделен процесс.
3.4 Основной процесс
Electron
Различают два вида процессов: основной процесс и процесс рендеринга, каждый из которых отвечает за свои функции.
Electron
бегатьpackage.json
изmain
Процесс скрипта называется основным процессом. ОдинElectron
Приложения всегда имеют один и только один главный процесс.
Обязанности:
- Создать процесс рендеринга (несколько)
- Управляет жизненным циклом приложения (запуск, выход)
APP
и правильноAPP
прослушать какое-нибудь событие) - Вызывать базовые функции системы и вызывать собственные ресурсы
Вызываемый API:
Node.js API
-
Electron
предусмотрен основной процессAPI
(включая некоторые системные функции иElectron
Дополнительные возможности)
3.5 Процесс рендеринга
так какElectron
использовалChromium
показыватьweb
страница, так чтоChromium
Также используется многопроцессорная архитектура. каждыйElectron
серединаweb
Страница работает в собственном процессе рендеринга.
Основной процесс использует экземпляр BrowserWindow для создания страницы. Каждый экземпляр BrowserWindow запускает страницу в своем собственном процессе рендеринга. Когда экземпляр BrowserWindow уничтожается, соответствующий процесс рендеринга также прекращается.
Вы можете думать о процессе рендеринга как об окне браузера, он может существовать несколько и независимо друг от друга, но в отличие от браузера, он может вызыватьNode API
.
Обязанности:
- использовать
HTML
иCSS
визуализировать интерфейс - использовать
JavaScript
Сделайте некоторое взаимодействие с интерфейсом
Вызываемый API:
DOM API
Node.js API
-
Electron
предоставленный процесс рендерингаAPI
4. Электронные основы
4.1 Electron API
В предыдущем разделе мы упомянули, что рендерер и основной процесс можно вызывать соответственно.Electron API
. всеElectron
изAPI
Все они назначены типу процесса. многиеAPI
можно использовать только в основном процессе, некоторыеAPI
Его можно использовать только в процессе рендеринга, и можно использовать некоторые основные процессы и процессы рендеринга.
Вы можете получить его следующим образомElectron API
const { BrowserWindow, ... } = require('electron')
Ниже приведены некоторые часто используемыеElectron API
:
В следующих главах мы выберем часто используемые модули для подробного ознакомления.
4.2 Использование API Node.js
вы можете в то же времяElectron
Использование основного процесса и процесса визуализацииNode.js API
,) все вNode.js
годный к употреблениюAPI
,существуетElectron
также можно использовать в .
import {shell} from 'electron';
import os from 'os';
document.getElementById('btn').addEventListener('click', () => {
shell.showItemInFolder(os.homedir());
})
Одно очень важное замечание: нативные модули Node.js (то есть модули, которые необходимо скомпилировать из исходного кода, прежде чем их можно будет использовать) необходимо скомпилировать, прежде чем их можно будет использовать с Electron.
4.3 Технологическая коммуникация
Хотя основной процесс и процесс рендеринга имеют разные обязанности, они также должны взаимодействовать и общаться друг с другом.
Например: в
web
нативное управление страницамиGUI
Ресурсы очень опасны и могут легко привести к утечке ресурсов. так вweb
страница, на которой не разрешены прямые вызовы нативныхGUI
СвязанныйAPI
. Если процесс рендеринга хочет быть нативнымGUI
Чтобы работать, вы должны связаться с основным процессом и запросить основной процесс для завершения этих операций.
4.4 Процесс рендеринга взаимодействует с основным процессом
ipcRenderer
ЯвляетсяEventEmitter
пример. Вы можете использовать некоторые из предоставляемых им методов для отправки синхронных или асинхронных сообщений из процесса визуализации в основной процесс. Также возможно получать сообщения, на которые отвечает основной процесс.
Представлен в процессе рендерингаipcRenderer
:
import { ipcRenderer } from 'electron';
Отправить асинхронно:
пройти черезchannel
Отправьте сообщение синхронизации основному процессу, которое может нести любые параметры.
Внутри параметр сериализуется как
JSON
, поэтому цепочка функций и прототипов для объекта параметра не отправляется.
ipcRenderer.send('async-render', '我是来自渲染进程的异步消息');
Отправить синхронно:
const msg = ipcRenderer.sendSync('sync-render', '我是来自渲染进程的同步消息');
Примечание. Отправка синхронного сообщения заблокирует весь процесс рендеринга до тех пор, пока не будет получен ответ от основного процесса.
Основной процесс прослушивает сообщения:
ipcMain
модульEventEmitter
Экземпляр класса. При использовании в основном процессе он обрабатывает асинхронные и синхронные сообщения, отправленные из процесса визуализации (веб-страницы). Сообщения, отправленные из процесса визуализации, будут отправлены в этот модуль.
ipcMain.on
:мониторchannel
, когда получено новое сообщениеlistener
Воляlistener(event, args...)
форма называется.
ipcMain.on('sync-render', (event, data) => {
console.log(data);
});
4.5 Основной процесс взаимодействует с процессом рендеринга
в основном процессе черезBrowserWindow
изwebContents
Отправьте сообщение процессу рендеринга, поэтому вы должны сначала найти соответствующий процесс рендеринга, прежде чем отправлять сообщение.BrowserWindow
объект. :
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.webContents.send('main-msg', `ConardLi]`)
Отправлено согласно источнику:
существуетipcMain
В функции обратного вызова, которая принимает сообщение, передайте первый параметрevent
свойстваsender
Вы можете получить исходный код процесса рендерингаwebContents
объект, мы можем напрямую ответить на сообщение с помощью этого объекта.
ipcMain.on('sync-render', (event, data) => {
console.log(data);
event.sender.send('main-msg', '主进程收到了渲染进程的【异步】消息!')
});
Монитор процесса рендеринга:
ipcRenderer.on
:мониторchannel
, когда приходит новое сообщение, оно будет передано черезlistener(event, args...)
перечислитьlistener
.
ipcRenderer.on('main-msg', (event, msg) => {
console.log(msg);
})
4.6 Принцип связи
ipcMain
иipcRenderer
обаEventEmitter
Экземпляр класса.EventEmitter
классNodeJS
событийная основа, состоящая изNodeJS
серединаevents
экспорт модуля.
EventEmitter
В основе этого лежит инкапсуляция функций запуска событий и прослушивания событий. Он реализует интерфейсы, требуемые моделью событий, в том числеaddListener,removeListener
, emit
и другие инструменты и методы.JavaScript
События аналогичны, с использованием подхода публикации/подписки (наблюдателя), с использованием внутренних_events
Список для записи зарегистрированных обработчиков событий.
мы проходимipcMain
иipcRenderer
изon、send
Прослушивание и отправка сообщенийEventEmitter
Определены связанные интерфейсы.
4.7 remote
remote
Модуль связывается с основным процессом для процесса рендеринга (веб-страница) (IPC
) обеспечивает простой способ. использоватьremote
модуль, вы можете позвонитьmain
методы объекта процесса без необходимости явно отправлять межпроцессные сообщения, аналогичныеJava
изRMI
.
import { remote } from 'electron';
remote.dialog.showErrorBox('主进程才有的dialog模块', '我是使用remote调用的')
Но на самом деле, когда мы вызываем метод или функцию удаленного объекта или создаем новый объект через удаленный конструктор, мы фактически отправляем синхронное межпроцессное сообщение.
пройти вышеremote
вызов модуляdialog
в примере. мы создаем в процессе рендераdialog
Объект на самом деле не находится в нашем процессе рендеринга, он просто позволяет основному процессу создать объект.dialog
объект и возвращает соответствующий удаленный объект в процесс рендеринга.
4.8 Рендеринг межпроцессного взаимодействия
Electron
Процессы рендеринга не могут взаимодействовать друг с другом, поэтому мы можем установить станцию ретрансляции сообщений в основном процессе.
Связь между процессами рендеринга сначала отправляет сообщение основному процессу, а ретрансляционная станция основного процесса получает сообщение и распределяет его в соответствии с условиями.
4.9 Обмен данными процесса рендеринга
Самый простой способ обмена данными между двумя процессами рендеринга — использовать то, что уже реализовано в браузере.HTML5 API
. Лучшим решением является использованиеStorage API
,localStorage,sessionStorage
илиIndexedDB。
Как и в браузере, это хранилище эквивалентно постоянному хранению части данных в приложении. Иногда вам не нужно такое хранилище, просто обмен данными на время жизни текущего приложения. В этот момент вы можете использоватьElectron
внутриIPC
реализация механизма.
Сохраняйте данные в глобальной переменной в основном процессе и используйте их в нескольких процессах рендеринга.remote
модуль для доступа к нему.
Инициализируйте глобальные переменные в основном процессе:
global.mainId = ...;
global.device = {...};
global.__dirname = __dirname;
global.myField = { name: 'ConardLi' };
Читать в процессе рендеринга:
import { ipcRenderer, remote } from 'electron';
const { getGlobal } = remote;
const mainId = getGlobal('mainId')
const dirname = getGlobal('__dirname')
const deviecMac = getGlobal('device').mac;
Изменено в процессе рендеринга:
getGlobal('myField').name = 'code秘密花园';
Несколько процессов рендеринга совместно используют глобальные переменные одного и того же основного процесса, так что может быть достигнут эффект совместного использования и передачи данных между процессами рендеринга.
5. Окно
5.1 BrowserWindow
основной технологический модульBrowserWindow
Используется для создания и управления окнами браузера.
mainWindow = new BrowserWindow({
width: 1000,
height: 800,
// ...
});
mainWindow.loadURL('http://www.conardli.top/');
ты сможешьздесьПосмотреть все параметры его конструкции.
5.2 Безрамные окна
Безрамочное окно — это окно без хрома, а части окна (например, панели инструментов) не являются частью веб-страницы.
существуетBrowserWindow
в конструктивных параметрахframe
Установить какfalse
Вы можете указать, что окно является окном без полей.После того, как панель инструментов будет скрыта, возникнут две проблемы:
- 1. Кнопки управления окнами (минимизация, полноэкранный режим, кнопка закрытия) будут скрыты
- 2. Невозможно перетащить мобильное окно
можно указать, указавtitleBarStyle
чтобы снова отображать кнопки панели инструментов, установите для него значениеhidden
Возвращает полноразмерное окно содержимого со скрытой строкой заголовка и по-прежнему имеет стандартные элементы управления окном в верхнем левом углу.
new BrowserWindow({
width: 200,
height: 200,
titleBarStyle: 'hidden',
frame: false
});
5.3 Перетаскивание окна
По умолчанию окна без полей нельзя перетаскивать. Мы можем использовать интерфейсCSS
Атрибуты-webkit-app-region: drag
Вручную укажите область перетаскивания.
В безрамочном окне поведение перетаскивания может конфликтовать с выделением текста, что можно изменить, установив-webkit-user-select: none;
Отключить выделение текста:
.header {
-webkit-user-select: none;
-webkit-app-region: drag;
}
Вместо этого установите внутри перетаскиваемой области
-webkit-app-region: no-drag
Затем вы можете указать конкретную область без перетаскивания.
5.4 Прозрачное окно
поставивtransparent
параметры установлены наtrue
, вы также можете сделать бескаркасное окно прозрачным:
new BrowserWindow({
transparent: true,
frame: false
});
5.5 Webview
использоватьwebview
метка наElectron
Встраивайте «чужой» контент в приложение. Иностранный контент включен вwebview
в контейнере. Встраивание страниц в ваше приложение может контролировать макет и перерисовку входящего контента.
иiframe
разные,webview
Запуск в другом процессе, отличном от приложения. Он не имеет таких же разрешений, как ваша веб-страница, и все взаимодействия между приложением и встроенным содержимым будут асинхронными.
6. Диалог
dialog
модуль обеспечиваетapi
для отображения собственных системных диалогов, таких как окно открытия файла,alert
коробка, такweb
Приложения могут предоставлять пользователям те же возможности, что и системные приложения.
Примечание: диалоговое окно является основным модулем процесса, вы можете использовать удаленный доступ, если хотите вызвать его в процессе рендеринга.
6.1 Сообщение об ошибке
dialog.showErrorBox
Используется для отображения модального диалогового окна с сообщением об ошибке.
remote.dialog.showErrorBox('错误', '这是一个错误弹框!')
6.2 Диалоги
dialog.showErrorBox
Используется для вызова системных диалогов, для которых можно указать несколько различных типов: "none
", "info
", "error
", "question
" или "warning
".
В Windows «вопрос» отображает тот же значок, что и «информация», если вы не используете параметр «значок» для установки значка. В macOS «предупреждение» и «ошибка» отображают один и тот же значок предупреждения.
remote.dialog.showMessageBox({
type: 'info',
title: '提示信息',
message: '这是一个对话弹框!',
buttons: ['确定', '取消']
}, (index) => {
this.setState({ dialogMessage: `【你点击了${index ? '取消' : '确定'}!!】` })
})
6.3 Файловый ящик
dialog.showOpenDialog
Используется для открытия или выбора системного каталога.
remote.dialog.showOpenDialog({
properties: ['openDirectory', 'openFile']
}, (data) => {
this.setState({ filePath: `【选择路径:${data[0]}】 ` })
})
6.4 Информационное окно
Рекомендуется использовать непосредственноHTML5 API
, который можно использовать только в процессе визуализации.
let options = {
title: '信息框标题',
body: '我是一条信息~~~',
}
let myNotification = new window.Notification(options.title, options)
myNotification.onclick = () => {
this.setState({ message: '【你点击了信息框!!】' })
}
7. Система
7.1 Получить системную информацию
пройти черезremote
Перейти к основному процессуprocess
объект, вы можете получить информацию о каждой версии текущего приложения:
-
process.versions.electron
:electron
Информация о версии -
process.versions.chrome
:chrome
Информация о версии -
process.versions.node
:node
Информация о версии -
process.versions.v8
:v8
Информация о версии
Получить текущий корневой каталог приложения:
remote.app.getAppPath()
использоватьnode
изos
Модуль получает текущую корневую директорию системы:
os.homedir();
7.2 Скопируйте и вставьте
Electron
который предоставилclipboard
Доступно как в процессе визуализации, так и в основном процессе для выполнения операций копирования и вставки в системный буфер обмена.
Запишите в буфер обмена как обычный текст:
clipboard.writeText(text[, type])
Получить содержимое буфера обмена в виде обычного текста:
clipboard.readText([type])
7.3 Скриншоты
desktopCapturer
Информация об источниках мультимедиа, используемых для захвата аудио и видео с рабочего стола. Его можно вызвать только во время процесса рендеринга.
Ниже приведен пример кода, который делает снимок экрана и сохраняет его:
getImg = () => {
this.setState({ imgMsg: '正在截取屏幕...' })
const thumbSize = this.determineScreenShotSize()
let options = { types: ['screen'], thumbnailSize: thumbSize }
desktopCapturer.getSources(options, (error, sources) => {
if (error) return console.log(error)
sources.forEach((source) => {
if (source.name === 'Entire screen' || source.name === 'Screen 1') {
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png')
fs.writeFile(screenshotPath, source.thumbnail.toPNG(), (error) => {
if (error) return console.log(error)
shell.openExternal(`file://${screenshotPath}`)
this.setState({ imgMsg: `截图保存到: ${screenshotPath}` })
})
}
})
})
}
determineScreenShotSize = () => {
const screenSize = screen.getPrimaryDisplay().workAreaSize
const maxDimension = Math.max(screenSize.width, screenSize.height)
return {
width: maxDimension * window.devicePixelRatio,
height: maxDimension * window.devicePixelRatio
}
}
8. Меню
Меню приложения может помочь нам быстро добраться до той или иной функции, не прибегая к ресурсам интерфейса клиента.В целом меню делится на два типа:
- Меню приложения: вверху приложения, доступно глобально
- Контекстное меню: Вы можете настроить отображение любой страницы, настроить вызов, например контекстное меню
Electron
предоставляет намMenu
Модуль используется для создания собственных меню приложений и контекстных меню, это основной модуль процесса.
ты можешь пройтиMenu
статический методbuildFromTemplate(template)
, используя пользовательский шаблон меню для создания объекта меню.
template
ЯвляетсяMenuItem
массив, давайте посмотримMenuItem
Несколько важных параметров:
-
label
: текст, отображаемый в меню -
click
: обработчик события после нажатия на меню -
role
: Системные предопределенные меню, например.copy
(копия),paste
(вставить),minimize
(минимизировать)... -
enabled
: указывает, включен ли элемент, это свойство можно изменить динамически. -
submenu
: подменю, а такжеMenuItem
массив
Рекомендация: Лучше указать любой пункт меню, роль которого соответствует стандартной роли, чем пытаться реализовать поведение в функции щелчка вручную. Встроенное ролевое поведение обеспечит наилучшее локальное взаимодействие.
Следующий пример представляет собой простое менюtemplate
.
const template = [
{
label: '文件',
submenu: [
{
label: '新建文件',
click: function () {
dialog.showMessageBox({
type: 'info',
message: '嘿!',
detail: '你点击了新建文件!',
})
}
}
]
},
{
label: '编辑',
submenu: [{
label: '剪切',
role: 'cut'
}, {
label: '复制',
role: 'copy'
}, {
label: '粘贴',
role: 'paste'
}]
},
{
label: '最小化',
role: 'minimize'
}
]
8.1 Меню приложения
использоватьMenu
статический методsetApplicationMenu
, чтобы создать меню приложения вWindows
иLinux
начальство,menu
Будет установлено как меню верхнего уровня каждого окна.
Примечание. Это приложение API должно вызываться после события готовности модуля.
Мы можем обрабатывать меню по-разному в зависимости от разных жизненных циклов приложений и разных систем.
app.on('ready', function () {
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})
app.on('browser-window-created', function () {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = false
})
app.on('window-all-closed', function () {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = true
})
if (process.platform === 'win32') {
const helpMenu = template[template.length - 1].submenu
addUpdateMenuItems(helpMenu, 0)
}
8.2 Контекстное меню
использоватьMenu
метод экземпляраmenu.popup
Настраиваемое всплывающее контекстное меню.
let m = Menu.buildFromTemplate(template)
document.getElementById('menuDemoContainer').addEventListener('contextmenu', (e) => {
e.preventDefault()
m.popup({ window: remote.getCurrentWindow() })
})
8.3 Ярлыки
В опциях меню мы можем указатьaccelerator
свойства, чтобы указать сочетания клавиш для операций:
{
label: '最小化',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
}
Кроме того, мы также можем использоватьglobalShortcut
зарегистрировать глобальные сочетания клавиш.
globalShortcut.register('CommandOrControl+N', () => {
dialog.showMessageBox({
type: 'info',
message: '嘿!',
detail: '你触发了手动注册的快捷键.',
})
})
CommandOrControl означает клавишу Command в macOS и клавишу Control в Linux и Windows.
9. Печать
Во многих случаях печать, используемая в программе, невидима для пользователя. И если вы хотите гибко управлять содержимым печати, вам часто нужно использовать принтер, чтобы предоставить намapi
Чтобы разработать снова, этот метод разработки очень громоздкий и сложный в разработке. Первое использование в бизнесеElectron
На самом деле, это использование его функции печати, поэтому я представлю здесь еще кое-что.
Electron
Предоставленный API-интерфейс печати может очень гибко управлять отображением параметров печати и может записывать содержимое печати через html.Electron
Существует два способа печати: один — напрямую вызвать принтер для печати, другой — распечатать наpdf
.
И есть два типа объектов, которые могут вызывать print:
- пройти через
window
изwebcontent
Объекту, использующему этот метод, требуется отдельное окно печати, которое можно скрыть, но коммуникационный вызов относительно сложен. - используя страницу
webview
Элемент вызывает печать, вы можете поставитьwebview
Он скрыт на вызываемой странице, а способ связи относительно прост.
Вышеуказанные два метода имеют обаprint
иprintToPdf
метод.
9.1 Вызов системной печати
contents.print([options], [callback]);
В конфигурации печати (варианты) всего три простых настройки:
-
silent
: Не отображать ли конфигурацию печати при печати (будет ли печатать без вывода сообщений) -
printBackground
: печатать ли фон -
deviceName
: имя устройства принтера
Во-первых, нам нужно настроить имя принтера, который мы используем, и мы должны сначала определить, доступен ли принтер, прежде чем вызывать print.
использоватьwebContents
изgetPrinters
Метод может получить список принтеров, которые были настроены на текущем устройстве.Обратите внимание, что конфигурация недоступна, но драйвер был установлен на этом устройстве.
пройти черезgetPrinters
Полученный объект принтера:электрон JS.org/docs/API/body…
Нас здесь волнуют только двое,name
иstatus
,status
за0
означает, что принтер доступен.
print
второй параметрcallback
Это обратный вызов, используемый для определения того, запущена ли задача печати, а не обратный вызов после завершения задачи печати. Следовательно, будет выдано общее задание на печать, и будет вызвана функция обратного вызова, и будут возвращены параметры.true
. Этот обратный вызов не определяет, действительно ли печать была успешной.
if (this.state.curretnPrinter) {
mainWindow.webContents.print({
silent: silent, printBackground: true, deviceName: this.state.curretnPrinter
}, () => { })
} else {
remote.dialog.showErrorBox('错误', '请先选择一个打印机!')
}
9.2 Печать в PDF
printToPdf
Использование основных иprint
то же самое, ноprint
Элементов конфигурации очень мало, иprintToPdf
Расширены многие свойства. Просматривая здесь исходный код, я обнаружил, что еще много не вставленных в документ, около 30, в том числе возможность настроить поля печати, печать верхних и нижних колонтитулов и т.д.
contents.printToPDF(options, callback)
callback
Функция вызывается после сбоя печати или после успешного завершения печати для получения информации об ошибке печати или включенияPDF
буфер данных.
const pdfPath = path.join(os.tmpdir(), 'webviewPrint.pdf');
const webview = document.getElementById('printWebview');
const renderHtml = '我是被临时插入webview的内容...';
webview.executeJavaScript('document.documentElement.innerHTML =`' + renderHtml + '`;');
webview.printToPDF({}, (err, data) => {
console.log(err, data);
fs.writeFile(pdfPath, data, (error) => {
if (error) throw error
shell.openExternal(`file://${pdfPath}`)
this.setState({ webviewPdfPath: pdfPath })
});
});
Печать в этом примере выполняется с помощью
webview
сделано, позвонивexecuteJavaScript
метод может быть динамически направленwebview
Вставьте печать.
9.3 Выбор двух схем печати
упоминалось выше, используяwebview
иwebcontent
Вы можете вызвать функцию печати, используяwebcontent
Для печати, во-первых, должно быть окно печати, это окно нельзя создать в любой момент для печати, которая более требовательна к производительности. Вы можете запускать его во время работы программы и делать мониторинг событий.
Этот процесс должен связаться с вызывающей стороной для печати.Общий процесс выглядит следующим образом:
Видно, что общение очень громоздкое, используяwebview
Печать может дать тот же эффект, но метод связи проще, потому что процесс рендеринга иwebview
Общение не обязательно должно проходить через основной процесс, это можно сделать следующими способами:
const webview = document.querySelector('webview')
webview.addEventListener('ipc-message', (event) => {
console.log(event.channel)
})
webview.send('ping');
const {ipcRenderer} = require('electron')
ipcRenderer.on('ping', () => {
ipcRenderer.sendToHost('pong')
})
ранее предназначенный дляELectron
распечатать написанноеDEMO
:electron-print-demoинтересно можетclone
Проверьте это.
9.4 Пакет функций печати
Ниже приведены несколько инкапсуляций функций инструментов для общих функций печати.
/**
* 获取系统打印机列表
*/
export function getPrinters() {
let printers = [];
try {
const contents = remote.getCurrentWindow().webContents;
printers = contents.getPrinters();
} catch (e) {
console.error('getPrintersError', e);
}
return printers;
}
/**
* 获取系统默认打印机
*/
export function getDefaultPrinter() {
return getPrinters().find(element => element.isDefault);
}
/**
* 检测是否安装了某个打印驱动
*/
export function checkDriver(driverMame) {
return getPrinters().find(element => (element.options["printer-make-and-model"] || '').includes(driverMame));
}
/**
* 根据打印机名称获取打印机对象
*/
export function getPrinterByName(name) {
return getPrinters().find(element => element.name === name);
}
10. Защита программы
10.1 Сбои
Мониторинг сбоев — важная функция защиты для каждой клиентской программы.
- 1. Своевременно загружайте журнал сбоев и будильник
- 2. Программа мониторинга аварийно завершает работу, предлагая пользователю перезапустить программу.
electron
предоставил намcrashReporter
Чтобы помочь нам зарегистрировать сбой, мы можем передатьcrashReporter.start
Чтобы создать отчет о сбоях:
const { crashReporter } = require('electron')
crashReporter.start({
productName: 'YourName',
companyName: 'YourCompany',
submitURL: 'https://your-domain.com/url-to-submit',
uploadToServer: true
})
При сбое программы журнал сбоев будет храниться во временной папке с именемYourName Crashes
в папке с файлами.submitURL
Используется для указания сервера загрузки журнала сбоев. Прежде чем запускать отчет о сбоях, вы можете сделать это, вызвавapp.setPath('temp', 'my/custom/temp')
API для настройки пути сохранения этих временных файлов. Вы также можете пройтиcrashReporter.getLastCrashReport()
чтобы получить дату последнего отчета о сбое иID
.
мы можем пройтиwebContents
изcrashed
Для отслеживания сбоя процесса рендеринга, а также сбоя некоторых основных процессов было протестировано срабатывание этого события. Так что мы можем согласно Господуwindow
Уничтожен он или нет, зависит от разной логики рестарта, вот логика всего краш-мониторинга:
import { BrowserWindow, crashReporter, dialog } from 'electron';
// 开启进程崩溃记录
crashReporter.start({
productName: 'electron-react',
companyName: 'ConardLi',
submitURL: 'http://xxx.com', // 上传崩溃日志的接口
uploadToServer: false
});
function reloadWindow(mainWin) {
if (mainWin.isDestroyed()) {
app.relaunch();
app.exit(0);
} else {
// 销毁其他窗口
BrowserWindow.getAllWindows().forEach((w) => {
if (w.id !== mainWin.id) w.destroy();
});
const options = {
type: 'info',
title: '渲染器进程崩溃',
message: '这个进程已经崩溃.',
buttons: ['重载', '关闭']
}
dialog.showMessageBox(options, (index) => {
if (index === 0) mainWin.reload();
else mainWin.close();
})
}
}
export default function () {
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.webContents.on('crashed', () => {
const errorMessage = crashReporter.getLastCrashReport();
console.error('程序崩溃了!', errorMessage); // 可单独上传日志
reloadWindow(mainWindow);
});
}
10.2 Свернуть в трей
Иногда мы хотим, чтобы пользователь не закрывал программу нажатием кнопки закрытия, а сворачивал программу в трей и выполнял настоящую операцию выхода в трее.
Во-первых, прослушайте событие закрытия окна, предотвратите поведение по умолчанию операции закрытия пользователя и скройте окно.
function checkQuit(mainWindow, event) {
const options = {
type: 'info',
title: '关闭确认',
message: '确认要最小化程序到托盘吗?',
buttons: ['确认', '关闭程序']
};
dialog.showMessageBox(options, index => {
if (index === 0) {
event.preventDefault();
mainWindow.hide();
} else {
mainWindow = null;
app.exit(0);
}
});
}
function handleQuit() {
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.on('close', event => {
event.preventDefault();
checkQuit(mainWindow, event);
});
}
На данный момент программу уже не найти, а в панели задач программы нет, поэтому нам нужно создать панель задач и следить за событиями.
Использование платформы Windows
ico
файл для лучших результатов
export default function createTray() {
const mainWindow = BrowserWindow.fromId(global.mainId);
const iconName = process.platform === 'win32' ? 'icon.ico' : 'icon.png'
tray = new Tray(path.join(global.__dirname, iconName));
const contextMenu = Menu.buildFromTemplate([
{
label: '显示主界面', click: () => {
mainWindow.show();
mainWindow.setSkipTaskbar(false);
}
},
{
label: '退出', click: () => {
mainWindow.destroy();
app.quit();
}
},
])
tray.setToolTip('electron-react');
tray.setContextMenu(contextMenu);
}
11. Возможность расширения
Во многих случаях ваше приложение должно взаимодействовать с внешними устройствами.Как правило, производители предоставляют вам комплекты разработки для аппаратных устройств.Эти комплекты разработки в основномC++
пиши, пользуйсяelectron
В случае разработки у нас нет возможности напрямую вызыватьC++
Возможности кода, которыми мы можем воспользоватьсяnode-ffi
для достижения этой функции.
node-ffi
предоставляет мощный набор инструментов дляNode.js
среда с использованием чистогоJavaScript
Вызовите интерфейс библиотеки динамической компоновки. Его можно использовать для создания привязок интерфейса для библиотек без использования каких-либоC++
код.
Уведомление
node-ffi
нельзя вызывать напрямуюC++
код, вам нужноC++
Код скомпилирован в виде динамической библиотеки: вWindows
следующийDll
,существуетMac OS
следующийdylib
,Linux
даso
.
node-ffi
нагрузкаLibrary
ограничено и может обрабатывать толькоC
СтильLibrary
.
Вот простой пример:
const ffi = require('ffi');
const ref = require('ref');
const SHORT_CODE = ref.refType('short');
const DLL = new ffi.Library('test.dll', {
Test_CPP_Method: ['int', ['string',SHORT_CODE]],
})
testCppMethod(str: String, num: number): void {
try {
const result: any = DLL.Test_CPP_Method(str, num);
return result;
} catch (error) {
console.log('调用失败~',error);
}
}
this.testCppMethod('ConardLi',123);
В приведенном выше коде мы используемffi
УпаковкаC++
Созданная интерфейсом библиотека динамических ссылокtest.dll
и использоватьref
Сделайте некоторое сопоставление типов.
использоватьJavaScript
При вызове этих методов сопоставления рекомендуется использоватьTypeScript
согласовать типы параметров, т.к. слабо типизированыJavaScript
При вызове интерфейсов на строго типизированных языках могут возникнуть непредвиденные риски.
С помощью этой возможности фронтенд-разработчики также могутIOT
Поле красуется 😎~
12. Экологический выбор
Как правило, наши приложения могут работать в нескольких средах (production
,beta
,uat
,moke
,development
...), разные среды разработки могут соответствовать разным внутренним интерфейсам или другим конфигурациям, мы можем создать простую функцию выбора среды в клиентской программе, чтобы помочь нам развиваться более эффективно.
Конкретные стратегии заключаются в следующем:
- В среде разработки мы напрямую заходим на страницу выбора окружения, читаем выбранное окружение и в ответ выполняем операцию перенаправления
- Сохранить запись выбора среды в меню для переключения во время разработки
const envList = ["moke", "beta", "development", "production"];
exports.envList = envList;
const urlBeta = 'https://wwww.xxx-beta.com';
const urlDev = 'https://wwww.xxx-dev.com';
const urlProp = 'https://wwww.xxx-prop.com';
const urlMoke = 'https://wwww.xxx-moke.com';
const path = require('path');
const pkg = require(path.resolve(global.__dirname, 'package.json'));
const build = pkg['build-config'];
exports.handleEnv = {
build,
currentEnv: 'moke',
setEnv: function (env) {
this.currentEnv = env
},
getUrl: function () {
console.log('env:', build.env);
if (build.env === 'production' || this.currentEnv === 'production') {
return urlProp;
} else if (this.currentEnv === 'moke') {
return urlMoke;
} else if (this.currentEnv === 'development') {
return urlDev;
} else if (this.currentEnv === "beta") {
return urlBeta;
}
},
isDebugger: function () {
return build.env === 'development'
}
}
Тринадцать, упаковка
Последний и самый важный шаг — упаковать написанный код в исполняемый файл..app
или.exe
запускаемый файл.
Здесь я делаю две части атмосферы упаковки, упаковку процесса рендеринга и упаковку основного процесса.
13.1 Пакетирование и обновление процесса рендеринга
В общем, большая часть нашего кода бизнес-логики завершается в процессе рендеринга. В большинстве случаев нам нужно только обновить и обновить процесс рендеринга без изменения основного кода процесса. Упаковка нашего процесса рендеринга фактически такая же, как и общая процесс.web
Нет большой разницы в упаковке проекта, используйтеwebpack
Просто упакуйте это.
Здесь я рассказываю о преимуществах упаковки процесса рендеринга отдельно:
в упаковкеhtml
иjs
файл, мы обычно загружаем его на наш внешний сервер статических ресурсов, а затем сообщаем серверу, что наш процесс рендеринга имеет обновление кода, которое можно назвать отдельным обновлением процесса рендеринга.
Обратите внимание, что, в отличие от обновлений оболочки, обновления средства визуализации доступны только на сервере статических ресурсов.html
иjs
Обновление файла не требует повторной загрузки клиента обновления, поэтому каждый раз, когда мы запускаем программу и обнаруживаем, что в автономном пакете есть обновление, мы можем напрямую обновлять и читать последнюю версию файла статического ресурса, даже если программа запущена, нашей программе нужно только принудительно обновить страницу, чтобы прочитать последние статические ресурсы, такое обновление очень удобно для пользователя.
Обратите внимание, что раз мы настроили таким образом, это означает, что процесс рендеринга и основной процесс полностью отделены от упаковки и обновления.Файлы, которые мы читаем при запуске главного окна, больше не должны быть локальными файлами, а будут размещены на статике. сервер ресурсов после упаковки документ.
Для облегчения разработки здесь мы можем различать локальную и онлайн-загрузку разных файлов:
function getVersion (mac,current){
// 根据设备mac和当前版本获取最新版本
}
export default function () {
if (build.env === 'production') {
const version = getVersion (mac,current);
return 'https://www.xxxserver.html/electron-react/index_'+version+'.html';
}
return url.format({
protocol: 'file:',
pathname: path.join(__dirname, 'env/environment.html'),
slashes: true,
query: { debugger: build.env === "development" }
});
}
конкретныйwebpack
Конфигурация больше не будет здесь выкладываться, вы можете зайти в мойgithub
electron-react
из/scripts
Посмотреть в каталоге.
Здесь следует отметить, что в среде разработки мы можем комбинироватьwebpack
изdevServer
иelectron
команда для запускаapp
:
devServer: {
contentBase: './assets/',
historyApiFallback: true,
hot: true,
port: PORT,
noInfo: false,
stats: {
colors: true,
},
setup() {
spawn(
'electron',
['.'],
{
shell: true,
stdio: 'inherit',
}
)
.on('close', () => process.exit(0))
.on('error', e => console.error(e));
},
},//...
13.2 Основная технологическая упаковка
Основной процесс состоит в том, чтобы упаковать всю программу в работающую клиентскую программу.Обычно используются две схемы упаковки.electron-packager
иelectron-builder
.
electron-packager
Мне кажется немного громоздким упаковывать конфигурацию, и он может упаковать приложение только непосредственно как исполняемую программу.
Здесь я рекомендую использоватьelectron-builder
, он не только имеет удобную конфигурациюprotocol
функция, встроенныйAuto Update
, простая конфигурацияpackage.json
Вся работа по упаковке может быть завершена, и пользовательский опыт очень хороший. иelectron-builder
Приложение может быть не только упаковано непосредственно вexe app
и другие исполняемые программы, также могут быть упакованы вmsi dmg
и другие форматы установочных пакетов.
ты сможешьpackage.json
Удобно для различных конфигураций:
"build": {
"productName": "electron-react", // app中文名称
"appId": "electron-react",// app标识
"directories": { // 打包后输出的文件夹
"buildResources": "resources",
"output": "dist/"
}
"files": [ // 打包后依然保留的源文件
"main_process/",
"render_process/",
],
"mac": { // mac打包配置
"target": "dmg",
"icon": "icon.ico"
},
"win": { // windows打包配置
"target": "nsis",
"icon": "icon.ico"
},
"dmg": { // dmg文件打包配置
"artifactName": "electron_react.dmg",
"contents": [
{
"type": "link",
"path": "/Applications",
"x": 410,
"y": 150
},
{
"type": "file",
"x": 130,
"y": 150
}
]
},
"nsis": { // nsis文件打包配置
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"shortcutName": "electron-react"
},
}
воплощать в жизньelectron-builder
При упаковке команды вы можете указать параметры для упаковки.
--mac, -m, -o, --macos macOS打包
--linux, -l Linux打包
--win, -w, --windows Windows打包
--mwl 同时为macOS,Windows和Linux打包
--x64 x64 (64位安装包)
--ia32 ia32(32位安装包)
Для обновлений основного процесса вы можете использоватьelectron-builder
автономныйAuto Update
модуль, вelectron-react
Так же реализован модуль ручного обновления.Ввиду нехватки места я не буду их здесь повторять.Если интересно,можете зайти на мойgithub
Проверятьmain
внизupdate
модуль.
13.3 Оптимизация упаковки
electron-builder
в упаковкеApp
Оно намного больше, чем нативное клиентское приложение с той же функцией.Даже если это пустое приложение, объем должен быть в100mb
выше. есть много причин:
Первый момент: для достижения кроссплатформенного эффекта каждыйElectron
Приложение содержит всюV8
двигатель иChromium
ядро.
Второй момент: при упаковке всеnode_modules
Упаковано, все знают приложениеnode_module
Объем очень большой, что также делаетElectron
Причина в большом размере запакованного приложения.
Первый пункт мы не можем изменить, мы можем оптимизировать объем приложения из второго пункта:Electron
Только при упаковкеdenpendencies
зависимости упакованы безdevDependencies
Зависимости в пакете упакованы. Поэтому мы должны максимально уменьшитьdenpendencies
зависимости в. В описанном выше процессе мы используемwebpack
Упакуйте процесс рендеринга, чтобы все зависимости процесса рендеринга можно было переместить вdevDependencies
.
В качестве альтернативы мы также можем использовать doublepackajson.json
Способ оптимизации, поместить зависимости, используемые только в среде разработки, в корневую директорию всего проекта.package.json
Затем установите зависимости, связанные с платформой или требуемые во время выполнения, вapp
Под содержанием. Подробнее см.two-package-structure.
Ссылаться на
- electronjs.org/docs
- называется lord.US/essential - о...
- IM Web.IO/topic/5 не 9 сообщение 5…
- woohoo.brief.com/afraid/1 о, книга 6 против 7, о…
- zhuanlan.zhihu.com/p/52991793
Адрес исходного кода этого проекта:GitHub.com/con AR DL i/Harsh…
резюме
Я надеюсь, что вы сможете достичь следующих результатов после прочтения этой статьи:
- учиться
Electron
Основной принцип работы - владелец
Electron
Основные принципы развития - учиться
Electron
Об основном использовании всплывающих окон, печати, защиты, упаковки и других функций
Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь.
Если вы хотите читать больше качественных статей, вы можете подписаться на меняgithub
блог, твоя звезда✨, лайки и внимание - движущая сила моего постоянного творчества!
Рекомендую обратить внимание на мой паблик WeChat [code secret garden], каждый день выкладывать качественные статьи, будем общаться и расти вместе.
Подписавшись на официальную учетную запись, ответьте на [Добавить группу], чтобы включить вас в высококачественную группу внешнего интерфейса.