Щепотка перца
Зачем изучать электрон? Как интерфейсный веб-инженер, вы не должны брать на себя инициативу, чтобы изучить эту вещь.Это действительно ненужно.Разве не восхитительно делать веб! Ну, конечно, предыдущее обсуждение основано на вашем престиже и независимости в вашей команде (то есть на уровне тимлида), иначе просто сдайтесь! Как и я, два года назад, когда я впервые присоединился к компании, я не разбирался в Интернете, поэтому мой наставник уговорил меня сделать электронный проект самостоятельно. Хорошо!).
Подавать основное блюдо
Что такое электрон?
Electron предназначен для создания кроссплатформенных настольных приложений с использованием JavaScript, HTML и CSS. Эта статья основана на системе Windows.
Почему Electron кроссплатформенный?
Как показано на рисунке ниже, Electron состоит из хрома, nodejs и собственного API. Среди них nodejs — это среда выполнения JavaScript на основе движка Chrome V8, обеспечивающая поддержку различных системных платформ, и chromium — браузерный движок Google с открытым исходным кодом, который также обеспечивает поддержку различных системных платформ. Команда разработчиков Electron реализует кроссплатформенность Electron, наследуя chromium и nodejs из разных систем и предоставляя некоторые API-интерфейсы системного уровня, от которых зависят настольные приложения.
Электрон не пахнет
В эпоху Интернета на первое место выходит эффективность. С развитием Интернета, особенно с развитием клиентских технологий, был достигнут очень и очень большой прогресс в эффективности и качестве клиентской разработки.В то же время интерфейс имеет самое большое и лучшее в мире управление пакетами. Инструмент NPM, который предоставляет фронтенд-разработчикам бесчисленное множество высококачественных готовых ресурсов для разработки, которые недоступны на других языках разработки.
Недостатки традиционной разработки настольных приложений
Традиционная разработка настольных клиентов в основном использует C++ и C#, такие как MFC и QT C++, а winform C# — все это мощные инструменты для разработки настольных UI-клиентов. Хотя это хороший инструмент, у него есть и недостатки. Как мы все знаем, C++ и C# имеют высокие барьеры для входа, и их невозможно полностью интегрировать за три-пять лет, более того, эти языки могут не только разрабатывать клиентов, но и исследовать и реализовывать более низкоуровневые вещи, такие как Таланты, которые сосредоточены на разработке пользовательского интерфейса, еще более популярны. В отличие от внешнего интерфейса, порог низкий, а результаты быстрые. В то же время, цикл разработки пользовательского интерфейса с традиционной клиентской технологией очень долог, как и простая анимация загрузки, для достижения внешнего интерфейса требуется только анимация CSS3, а разработка с использованием технологии C++ займет очень много времени. время (если не используется gif). Поэтому неэффективные дела должны быть заменены эффективными делами.
истинный закон аромата
Разработка Electron наследует почти все преимущества разработки веб-интерфейса: высокая эффективность, идеальная экология, красивый интерфейс... Пока вы можете без препятствий использовать Javascript, вы можете начать работу с Electron за короткое время. Electron также может отображать онлайн-ресурсы, а ресурсы интерфейса хранятся на сервере для обеспечения эффективных и удобных обновлений, что является очень хорошим выбором для некоторых потребностей веб-клиента. Не будет преувеличением сказать: Electron — это фреймворк, действительно ориентированный на эпоху Интернета, позволяющий клиентской разработке итеративно разрабатывать и развертывать обновления так же быстро, как веб-разработка, приятно и вкусно!
на гриле
Установить
npm install --save-dev electron --arch=ia32|x64|arm64|armv7l
Поскольку для установки Electron требуется загрузить весь сжатый файл ресурсов электрона (около 70 МБ), первая скорость установки будет очень низкой.Рекомендуется использовать образ хранилища Taobao npm: --register=https://registry.npm.taobao. организация
arch: ia32 | x64 | arm64 | armv7l
- ia32: 32-разрядная версия архитектуры X86, 32-разрядная память, совместимая с 32-разрядными системами, обычная версия Windows использует это для соответствия 32-разрядным и 64-разрядным системам.
- X64: 64-битная микропроцессорная архитектура и соответствующий набор инструкций, который также является расширением архитектуры Intel X86
- arm64: 64-битная архитектура процессора ARM, в основном используемая в системах Linux.
- armv7l: 32-битная архитектура процессора ARM, в основном используемая в системах Linux.
Очевидно, что если проект разрабатывается несколькими людьми, этот --arch будет потерян на чужом устройстве, если только электрон не будет установлен снова, здесь этот параметр можно сохранить в package.json.
"config": {
"arch": "ia32"
}
Изысканная техника барбекю
создание окна
Настройте запись, настройте скрипт запуска
//package.json
{
"main": "index.js",
"scripts": {
"start": "electron ."
}
}
Время выполнения электронов имеет Package.json в качестве его ввода. Создайте окно ниже:
const { BrowserWindow } = require('electron');
let win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://google.com');
Конкретные значения параметров можно узнать в официальной документации, поэтому я не буду их здесь перечислять!
В чем разница между BrowserWindow и BrowserView
BrowserView используется, чтобы позволить BrowserWindow встраивать больше веб-контента. Это включает в себя концепцию веб-просмотра.
- webview — это элемент на веб-странице
- Роль webwiew заключается во внедрении чужих ненадежных ресурсов, изоляции чужих ресурсов от локальных ресурсов и обеспечении безопасности встроенных ресурсов.
- И роль ИФРМА аналогична, но ее сущность - это ее разница с IFrame WebView запущена отдельный процесс (то есть дочернее окно)
BrowserView — это альтернатива веб-просмотру, которая позволяет BrowserWindow динамически вводить веб-просмотр в основной процесс.
Разница между loadURL и loadFile
Самая большая разница между ними заключается в том, что loadURL может загружать адреса онлайн-ресурсов, а loadFile — нет.Оба могут загружать адреса локальных файлов.
В Windows7 свойство прозрачности настройки окна не действует, и окно отображается с цветом фона по умолчанию (черным).
Процессом системы Windows, отвечающим за реализацию состояния прозрачности окна, является процесс dwm.exe.Под основной частью win7, не являющейся аэродинамической, процесс находится в нерабочем или остановленном состоянии, и прозрачность окна не может быть реализована. Способы избежать:systemPreferences.isAeroGlassEnabled()
Программа, вызывающая экземпляр окна, сообщает об ошибке: Объект уничтожен
Electron вернет объект экземпляра окна при создании окна, чтобы разработчики могли работать с созданным окном в любое время. Однако окно будет уничтожено и закрыто по разным причинам, как показано в следующей таблице:
изящное завершение работы | Аварийное отключение |
---|---|
через виндовс апи |
Щелкните правой кнопкой мыши значок на панели задач, кнопку «Закрыть окно». |
Закройте соответствующий оконный процесс в списке процессов диспетчера задач. | |
Окно заблокировано некоторыми защитными программами. | |
Само окно вылетает |
Будь то нормальное или ненормальное завершение работы, если программа снова вызывает свойство функции или метод функции окна без синхронного уничтожения экземпляра объекта, соответствующего окну, программа выдаст исключение «Объект был уничтожен». ,тест небольшой.Моя сестра даст вам серьезный баг! Есть два способа предотвратить возникновение этого исключения:
1,задняя пушка: Сначала определите, уничтожается ли окно при вызове свойств окна.
//Electron BrowserWindow 实例对象提供有isDestroyed的方法查询窗口是否被销毁
if(!window.isDestroyed()){
window.hide();
}
//或
try {
window.hide();
} catch(error => {})
Если ваше приложение не имеет только одного окна, этот метод хорош, но если ваша программа имеет много окон, этот метод очень громоздкий, совершенно неприятный для программирования, и его даже легко пропустить.
2,превентивный: Окно прослушивания уничтожается, и объект экземпляра окна уничтожается синхронно. Экземпляр окна BrowserWindow имеет обратный вызов события для отслеживания закрытия окна, посредством которого экземпляр окна может быть уничтожен. Следующий код реализован способом, предоставленным BrowserWindow:
window.on('closed', () => {
delete window;
})
Эта реализация кода выглядит неуклюже.
Лучшей реализацией является разработка класса окна на основе шаблона фабрики и шаблона посредника для реализации таких функций, как создание окна, управление доступом к экземплярам окна и синхронное уничтожение экземпляров окна.
//WindowController
class WindowController {
constructor() {
this.windowInstances = {}
}
//创建窗口,从配置中获取窗口配置
createWindow(wname) {
let window = new BrowserWindow(config[wname]);
//窗口实例保存
this.windowInstances[wname] = window;
//监听窗口被销毁
window.on('closed', ((_wname) => {
return () => {
delete this.windowInstances[_wname];
}
})(wname));
//监听窗口内容崩溃
window.webContents.on('crashed', ((_wname) => {
return ()=> {
this.windowInstances[_wname].close();
this.createWindow(_wname)
}
})(wname))
}
//获取窗口实例
getWindow(wname) {
return this.windowInstances[_wname];
}
}
Окно не полностью вверху
При создании окна вы можете установить свойство окна alwaysOnTop: true или запретить setAlwaysOnTop, вызвав экземпляр окна для достижения верхней части окна. Этот метод наложения по существу устанавливает свойство topMost окна (Подробности), в общем, этого атрибута top достаточно, но если вы хотите, чтобы ваше окно располагалось поверх всех окон, этот атрибут полностью не поддерживается.
Если он установлен на верхний уровень в диспетчере задач системы Windows10, другие окна topMost не смогут запускаться на него. Чтобы достичь полной вершины, нам нужно ввести свойство, о котором не знают многие крупные шишки C++: изоляцию привилегий пользовательского интерфейса. Самое большое влияние этого свойства на окно заключается в том, что когда для окна задано значение topMost, окно помещается вверху, даже выше, чем интерфейс экрана блокировки системы в Windows10. А вот под вин7 это не так (почти бесполезно), под вин7 можно только зациклить до верха, а эффект нехороший. Для изоляции привилегий пользовательского интерфейса см.:UIAccess
Как опустить окно
Electron не предоставляет соответствующего метода нижней части окна или API, но некоторые резидентные реализации окон на рабочем столе требуют нижней части окна. Как этого добиться? Полагаться на собственную конфигурацию окна Electron нельзя полностью реализовать, это может быть только простой внешний вид. Есть два явления, когда окно опущено вниз.Во-первых, его нельзя активировать, поэтому метод show окна не может быть вызван.Вы должны использовать showInactive;
Во-вторых, окно не может получить фокус.Если окно может получить фокус, оно перейдет к другим окнам.
focusable: false
С этими двумя свойствами окно будет медленно опускаться вниз после его создания (поскольку окно не будет опускаться автоматически после создания, оно будет заменено только после того, как другие окна будут активированы или сфокусированы). Так как же подняться на вершину?
Нужно использовать третьими лицами! Рабочий стол системы также является окном. Это полностью выбранное окно, просто подключает окно в электроне, чтобы стать его подсчетом ниже окна рабочего стола, и установить два метода атрибутов, описанных выше, он полностью завершен. Существует способ установки родительского окна в электроне.
/**
* parent BrowserWindow | null
* 设置 parent 为当前窗口的父窗口. 为null时表示将当前窗口转为顶级窗口
/
win.setParentWindow(parent)
Однако окно рабочего стола не относится к примеру BrowserWindow. Так что вам нужно реализовать эту функцию с помощью библиотеки динамической компоновки, написанной на C++. Вот @c++ большой 佬.
Как установить значок окна на панели задач
Вообще говоря, после создания окна будет значок окна, соответствующий окну на панели задач.Если значок не установлен, он извлечет значок исполняемой программы, где текущее окно находится по умолчанию, то есть , значок, соответствующий файлу exe.
Конечно, мы можем настроить значок окна
win.setIcon(iconPath)
Однако просто установить его таким образом было бы проблематично
Имя приложения в контекстном меню значка на панели задач отображается некорректно, а значок, который остается на панели задач при включенной функции закрепления на панели задач, неверен (окно, которое запускается при нажатии на этот значок, также некорректно)
Вам нужно использовать оконный API setAppdetails
win.setAppDetails(options)
· options Object
· appId String (可选) - 窗口的 App User Model ID. 该项必须设置, 否则其他选项将没有效果.
· appIconPath String (可选) -窗口的 Relaunch Icon.
· appIconIndex Integer (optional) - Index of the icon in appIconPath. Ignored when appIconPath is not set. Default is 0.
· relaunchCommand String (可选) - 窗口的 重新启动命令.
· relaunchDisplayName String (可选) - 窗口的重新启动显示名称.
в:
- В качестве идентификатора приложения используется appId, а формат записи «название компании. название продукта. номер версии», а в качестве уникального идентификатора приложения используется appId, и его лучше не менять;
- relaunchDisplayName относится к имени, отображаемому в контекстном меню значка окна на панели задач;
- relaunchCommand относится к адресу в контекстном меню значка на панели задач окна, которое закреплено на панели задач, и щелкает значок для выполнения после включения функции и может принимать параметры;
пример:
let exePath = app.getPath('exe');
window.setAppDetails({
appId: 'Juejin.qianduanshaokaotan.v20190118001',
appIconPath: iconPath,
appIconIndex: 0,
relaunchDisplayName: '前端烧烤摊',
relaunchCommand: '"' + exePath + '"', //加参数的意义是C盘program files文件夹有空格风险,导致路由错误
});
После вызова программой setAppDetails будет создана соответствующая папка с ярлыками по адресу C:\Users\xx\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\ImplicitAppShortcuts Имя папки меняется вместе с appId. и изменился.
Этот ярлык указывает на адрес исполняемой программы.Как и ярлыки на рабочем столе, когда исполняемая программа потеряна или удалена, соответствующий трансграничный метод будет недействительным и его необходимо будет удалить вручную, поэтому ярлык, созданный setAppDetails, лучше всего использовать, когда программа удален. Удалите его, если хотите предотвратить неприятный опыт!
Как перетащить окно без рамки
По сравнению с окнами без полей, окно с рамкой имеет на одну панель инструментов и строку заголовка больше, а строка заголовка позволяет окну иметь возможность перетаскивания с помощью мыши. Однако окно без полей не имеет строки заголовка, поэтому его нельзя перетаскивать и перемещать, что требует реализации кода.
первый способ**** Стиль узла Dom, который будет имитировать область строки заголовка, установлен -webkit-app-region:drag После установки этого свойства внутренний узел узла (сам пакет) не будет реагировать на событие клика. , Если вы хотите, чтобы внутренний узел реагировал на событие щелчка, установите -webkit-app-region: no-drag в стиле внутреннего узла.
<div style="-webkit-app-region: drag;">
<span>标题栏</span>
<button style=": no-drag;">按钮</button>
</div>
Однако этот метод не может обеспечить удобный функциональный шар на рабочем столе, такой как 360 Security Guard, и его нельзя перетаскивать и щелкать для быстрого переключения.
второй способИспользуйте собственную реализацию событий html. Здесь рекомендуется комбинация mousedomn+mousemove+mouseup.Перетаскивание HTML (Drag and Drop) конечно возможно, но управлять им не так просто!
index.html
<div id="header">我是标题栏</div>
<script>
let headerDom = document.getElementById('header');
let isMoving = false,
isClick = false,
startX = 0,
startY = 0;
headerDom.onmousedown = (e) => {
isMoving = false;
isClick = true;
startX = e.clientX;
startY = e.clientY;
};
document.onmousemove = (e) => {
let distanceX = e.clientX - startX;
let distanceY = e.clientY - startY;
if (isClick) {
//窗口移动
//发送窗口位置到主进程进行处理
ipcRenderer.send('windowMove', {distanceX, distanceY});
isMoving = true;
}
};
headerDom.onmouseup = () => {
isClick = false;
if (!isMoving) {
//单纯点击
//处理点击事件
} else {
isMoving = false;
}
};
</script>
ipcMain.js
ipcMain.on('windowMove', (event, position) => {
let wx = Math.ceil(position.distanceX + window.getPosition()[0]);
let wy = Math.ceil(position.distanceY + window.getPosition()[1]);
if (wx > 0 && wy > 0) {
window.setPosition(wx, wy);
}
})
Игра с переключателями командной строки хрома
Ядро хрома в Electron поддерживает создание окон и рендеринг контента, а ядро хрома имеет множество настроек по умолчанию для рендеринга веб-страниц, таких как необходимость авторизации пользователя для вызова камеры в веб-контенте, а также автоматический звук и проигрывание видео.
Официальный сайт Electron предоставляет только некоторые командные строки, но хром предоставляет множество команд (Смотри сюда).
Итак, как вы узнаете, когда вам нужно искать эти параметры командной строки, чтобы помочь в разработке?
Пока функция страницы окна не соответствует настройкам или стандартам браузера, вы можете попробовать найти соответствующий коммутатор Chromium Command для решения фактической проблемы.
//触摸屏上会出现点击范围扩大的问题,这个可以解决
app.commandLine.appendSwitch('disable-touch-adjustment', true);
//chrome 66以上版本屏蔽自动播放限制设置,音视频随心放
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
//触摸屏上禁用双指缩放
app.commandLine.appendSwitch('disable-pinch', true);
//关闭网络代理
app.commandLine.appendSwitch('no-proxy-server', true);
//关闭浏览器的证书拦截
app.commandLine.appendSwitch('ignore-certificate-errors', true);
Играть в Node-FFI
Электрон должен сосредоточиться на каркасе для дисплея пользовательского интерфейса, поэтому для некоторых более низкоуровневых функций не могут быть поддерживаться. Тогда вы должны использовать узел-FFI.
node-ffi — это сторонний модуль npm для nodejs для вызова библиотек динамической компоновки.
Так что же такое динамически подключаемая библиотека? Динамическая библиотека — это двоичный файл исполняемого кода, который может быть загружен в память операционной системой для выполнения, например файлы .dll в системах Windows и файлы .so в системах Linux.
Как упоминалось выше, окончательный способ реализовать нижнюю часть окна — позволить C++ реализовать его, а взаимодействие между функциями nodejs и C++ может быть реализовано в виде библиотеки динамической компоновки. Конечно, самый важный носитель — это node-ffi.
Установить узел-ffi
$ npm install ffi
Поскольку исходный код node-ffi представляет собой несколько языковых файлов C, node-gyp необходимо компилировать синхронно во время установки, поэтому вам необходимо правильно установить node-fyp перед установкой node-ffi.
руководство по установке node-gypСмотри сюда
Установить электронное восстановление
npm install --save-dev electron-rebuild
Причина установки electron-rebuild заключается в том, что модуль node-ffi, скомпилированный node-gyp, может отличаться по версии и архитектуре от nodejs в Electron, используемом в проекте, и для перекомпиляции node-ffi требуется электрон-rebuild, прежде чем он сможет использоваться. .
Установить ref, ref-array, ref-struct
Три модуля ref, ref-array и ref-struct являются оболочками для некоторых типов параметров в C++.Экспортированные функции dll могут быть определены в ffi как типы параметров.
Вызов динамически подключаемых библиотек с помощью node-ffi
Определения конкретных параметров см.официальный гитхаб
простой вызов
#use-ffi.js
ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, []]
})
Параметр функции — это функция обратного вызова.
Помните, что функция обратного вызова является указателем, поэтому вам необходимо указать тип «указатель» при инициализации экспортируемых параметров функции.
//定义导出函数
let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, ['pointer']]
});
//使用ffi.Callback初始化回调函数
const newCallback = (func) => {
/**
* ffi.Callback的第一参数是回调函数的返回类型,第二个是回调函数中的参数类型,第三个是回调函数
*/
return ffi.Callback(ref.types.void, [], func)
}
// 保存回调函数,防止内存回收
let callbackStore = [];
let callBack = newCallback(() => { console.log('函数回调')});
callbackStore.push(callBack);
process.on('exit', () => {
delete callbackStore
});
//调用导出函数
dllFunc.MyFunction(callBack);
Указатель передается как параметр
//定义导出函数,ref.refType将基础类型转成指针类型
let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, [ref.refType(ref.types.int)]]
});
//调用导出函数,使用Buffer.alloc开辟内存
let myParams = new Buffer.alloc(100);
dllFunc.MyFunction(myParams);
Структура передается в качестве параметра
//ref-struct初始化结构体
const myStruct = Struct({
id: ref.types.int
});
//定义导出函数,ref.refType将结构体类型转成指针结构体类型
let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, [ref.refType(myStruct)]]
});
//调用导出函数,通过.ref()获取结构体内存首地址,方便dll内函数复写
dll.MyFunction(myStruct.ref());
Массив передается как параметр
//ref-array定义数组
const MyArray = RefArray(ref.types.int);
//定义导出函数,不需要传入数组定义,只需要传入数组成员定义
let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, [ref.refType(ref.types.int)]]
});
//初始化数组
let myArray = new MyArray(10)
for(let i = 0; i < 10; i++) {
myArray[i] = 0;
}
//调用导出函数,传入第一个成员内存地址
dllFunc.MyFunction(myArray[0].ref());
Совет: получите дескриптор окна Electron и преобразуйте его в целое число.
//deref方法获取指针对应的值
let windowHandle = window.getNativeWindowHandle();
let _handle = Buffer.from(windowHandle);
_handle.type = ref.types.int;
dllFunc.MyFunction(handle.deref())
Играйте с электронной упаковкой
Упомянутая здесь электронная упаковка разделена на две части: одна часть — упаковка кода, а другая часть — упаковка программы.
Смысл упаковки кода:
- Уменьшение размера программы установки позволяет дополнительно уменьшить пропускную способность сети, занимаемую программой при ее загрузке и автоматическом обновлении.
- Улучшить безопасность исходного кода. Поскольку исходный код в программе Electron не имеет каких-либо мер шифрования, исходный код легко извлечь, поэтому исходный код необходимо упаковать, сжать и запутать, чтобы уменьшить читаемость кода и повысить безопасность кода.
упаковка кода
Здесь мы используем webpack для упаковки кода.
webpack предоставляет два метода упаковки кода для проектов Electron: target: 'electron-renderer' | 'electron-main', которые упаковываются для процесса рендеринга и основного процесса соответственно, а импортированные модули nodejs и электронные модули будут автоматически игнорироваться.
Упаковка кода основного процесса не так проста, например, нативные модули nodejs, которые будут использоваться в основном процессе, такие как упомянутые выше модули node-ffi и ref, нуждаются в специальной обработке файлов .node, созданных после этих модули компилируются. Здесь node-loader используется для обработки файлов .node.
//webpack.config.js
module.exports = {
entry: './main.js',
mode: 'production',
target: 'electron-main',
//防止打包之后__dirname改变
node: {
__dirname: false,
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
},
module: {
rules: [
{
test: /\.node$/,
use: [
{
loader: 'node-loader',
options: {
name: '/[path][name].[ext]',
},
},
],
},
],
},
}
После выполнения упаковки не сообщается об ошибке, вроде все прошло успешно, но после запуска кода упаковки обнаруживается, что сообщается об ошибке, что происходит?
При этом было обнаружено, что файл .node не экспортируется в папку dist.
Найдите причину и обнаружите, что внедрение ffi_bindings.node в ffi не является традиционным способом, модуль привязок получает файл .node по удобному адресу.
//node_modules/ffi/lib/bindings.js
module.exports = require('bindings')('ffi_bindings.node')
//node_modules/bindings/bindings.js
...
try: [
// node-gyp's linked version in the "build" dir
[ 'module_root', 'build', 'bindings' ]
// node-waf and gyp_addon (a.k.a node-gyp)
, [ 'module_root', 'build', 'Debug', 'bindings' ]
, [ 'module_root', 'build', 'Release', 'bindings' ]
// Debug files, for development (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Debug', 'bindings' ]
, [ 'module_root', 'Debug', 'bindings' ]
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
, [ 'module_root', 'out', 'Release', 'bindings' ]
, [ 'module_root', 'Release', 'bindings' ]
// Legacy from node-waf, node <= 0.4.x
, [ 'module_root', 'build', 'default', 'bindings' ]
// Production "Release" buildtype binary (meh...)
, [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ]
]
Поскольку модуль node-loader может обрабатывать файлы только в форме require/import, форма импортированных файлов, такая как ffi, не может быть обработана, поэтому сообщается об ошибке.
Решение состоит в том, чтобы переписать модуль привязок.В веб-пакете есть конфигурация resolve.alias для установки псевдонима модуля, на который ссылаются, который может изменить адрес приложения модуля, на который ссылаются.
//webpack.config.js
...
resolve: {
alias: {
bindings: path.resolve(__dirname, 'replaceBindings.js')
}
}
//replaceBindings.js
modules.exports = function(filename){
if(filename === "ffi_bindings.node") {
return require('ffi/build/Release/ffi_bindings.node')
}
}
Перепакуйте, программа работает без исключения, и проблема решена!
Упаковка программы
Упаковка электронной программы относится к дальнейшей упаковке кода в программу, которую можно распространять, загружать, устанавливать, а также быстро запускать и запускать.
Наиболее часто используемые модули упаковки программ Electron — это electronic-packager и electronic-builder. Здесь рекомендуется использовать Electron-Builder, так как по сравнению с electronic-packager у Electron-Builder более полные функции и более понятная документация.
Процесс упаковки электронного строителя:
Используйте форму API для электронного компоновщика, чтобы упаковать пример:
const electronBuilder = require('electron-builder');
const child_process = require('child_process');
const path = require('path');
//获取git提交数作为版本号
child_process.execSync('rm -rf dist');
const version = child_process.execSync('git rev-list HEAD | wc -l').toString().trim();
const baseVersion = require('./package.json').version;
const buildVersion = baseVersion + '.' + version;
const semverVersion = baseVersion + '-' + version;
electronBuilder.argv = 'x64';
const targetDist = path.resolve(__dirname, 'dist');
const baseOptions = {
//应用唯一标识
appId: 'shaokaotan.demo.demo',
buildVersion: buildVersion,
//更替根目录下package.json中对应信息
extraMetadata: {
//应用产品版本号,用于
productVerion: buildVersion,
author: {
name: 'Shaokaotan',
email: 'www.Shaokaotan.com',
url: 'www.Shaokaotan.com',
},
version: semverVersion,
},
productName: 'MyProgram',
copyright: 'Copyright (C) 2020. Shaokaotan Electronics. All Rights Reserved.',
directories: {
buildResources: path.resolve(__dirname, '.'),
output: targetDist,
},
nsis: {
oneClick: false,
perMachine: true,
allowElevation: false,
allowToChangeInstallationDirectory: true,
installerIcon: path.resolve(__dirname, './icons/favicon.ico'),
uninstallerIcon: path.resolve(__dirname, './icons/favicon.ico'),
installerHeaderIcon: path.resolve(__dirname, './icons/favicon.ico'),
createDesktopShortcut: true,
createStartMenuShortcut: true,
shortcutName: 'MyProgram',
artifactName: 'MyProgram' + buildVersion + '.${ext}',
uninstallDisplayName: 'MyProgram',
include: path.resolve(__dirname, 'install.nsh'),
},
win: {
icon: path.resolve(__dirname, './icons/favicon.ico'),
target: {
target: 'nsis',
arch: 'x64',
},
//需要打包的文件列表
files: ['!build.js'],
extraResources: [path.resolve(__dirname, 'icons/favicon.ico')],
publish: {
provider: 'generic',
channel: 'winLatest_' + buildVersion,
url: 'www.Shaokaotan.com',
},
//复制文件
extraFiles: [
{
from: path.resolve(__dirname, 'icons/favicon.ico'),
to: './resources/icons/favicon.ico',
},
],
},
};
electronBuilder.createTargets(['--win'], null, 'x64');
process.env.BUILD_NUMBER = version;
electronBuilder.build({
config: baseOptions,
});
Соответствие части приведенной выше информации в практических приложениях
Сравнение buildVersion и semverVersion
- buildVersion — это обычный формат версии в системе Windows: *.*.*.*
- SEMVERVERSIONS - это семантическая версия номер: *. *. * [- *], который в основном используется в интернет-приложениях, таких как поле версии Package.json модуля JS, является семантической версией
Итак, вы можете видеть, что поле версии extraMetadata в конфигурации использует semverVersion вместо buildVersion.
После того, как приложение будет установлено, оно также должно корректно отображаться в программах и функциях системной панели управления Windows (если только вы не будете тестировать невнимательно).
В конфигурации электрон-билдера нет элемента, предоставляющего эту информацию.После некоторых поисков эта информация поступает из системного реестра, поэтому необходимо сохранить эту информацию в указанном месте реестра. Адрес, по которому регистрационный знак хранит эту информацию,
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
Как это настроить? Это касается NSIS.В приведенной выше конфигурации nsis.include может добавлять пользовательские файлы nsis:
!include "FileFunc.nsh"
#获取版本号
!getdllversion "${BUILD_RESOURCES_DIR}\dist\win-unpacked\MyProgram.exe" expv_
!macro customInstall
#写入icon地址
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "DisplayIcon" "$INSTDIR\resources\icons\favicon.ico"
#写入支持链接地址
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "URLInfoAbout" "www.Shaokaotan.com"
#写入版本信息
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "DisplayVersion" "${expv_1}.${expv_2}.${expv_3}.${expv_4}"
!macroend
!macro customUnInstall
!macroend
Играйте с автоматическими обновлениями
См. Автообновление электронного билдераруководство.
Эм! После прочтения так и не понял. Пожалуйста, смотрите разбивку:
Сначала нарисуйте грубую блок-схему:
Чтобы завершить этот процесс, рабочая нагрузка рукописного ввода кажется небольшой, но необходимо учитывать множество граничных вопросов, поэтому для завершения этого процесса электрон-билдер предоставляет независимый модуль электрон-апдейтер.
Установить электрон-апдейтер
npm install --save electron-updater
Клиент вызывает автоматическое обновление
код представлен
const log = require('electron-log');
const { autoUpdater } = require('electron-updater');
const { updateWindow } = require('./window');
const { app,ipcMain } = require('electron');
//设置日志打印
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
//是否自动下载更新,设置为false时将通过api触发更新下载
autoUpdater.autoDownload = false;
//是否允许版本降级,也就是服务器版本低于本地版本时,依旧以服务器版本为主
autoUpdater.allowDowngrade = true;
//设置服务器版本最新版本查询接口配置
autoUpdater.setFeedURL({
provider: 'generic',
url: 'https://www.shaokaotan.com/autoUpdate/',
channel: 'myProgram',
});
//保存是否需要安装更新的版本状态,因为需要提供用户在下载完成更新之后立即更新和稍后更新的操作
let NEED_INSTALL = false;
//调用api检查是否用更新
const checkUpdate = () => {
autoUpdater.checkForUpdatesAndNotify().then((UpdateCheckResult) => {
log.info(UpdateCheckResult, autoUpdater.currentVersion.version);
//判断版本不一致,强制更新
if (UpdateCheckResult && UpdateCheckResult.updateInfo.version !== autoUpdater.currentVersion.version) {
//调起更新窗口
updateWindow();
}
});
};
//api触发更新下载
const startDownload = (callback, downloadSuccessCallback) => {
//监听下载进度并推送到更新窗口
autoUpdater.on('download-progress', (data) => {
callback && callback instanceof Function && callback(null, data);
});
//监听下载错误并推送到更新窗口
autoUpdater.on('error', (err) => {
callback && callback instanceof Function && callback(err);
});
//监听下载完成并推送到更新窗口
autoUpdater.on('update-downloaded', () => {
NEED_INSTALL = true;
downloadSuccessCallback && downloadSuccessCallback instanceof Function && downloadSuccessCallback();
});
//下载更新
autoUpdater.downloadUpdate();
};
//监听窗口发送来的进程消息,开始下载更新
ipcMain.on('startDownload', (event) => {
startDownload(
(err, progressInfo) => {
if (err) {
//回推下载错误消息
event.sender.send('update-error');
} else {
//回推下载进度消息
event.sender.send('update-progress', progressInfo.percent);
}
},
() => {
//回推下载完成消息
event.sender.send('update-downed');
}
);
});
//监听用户点击更新窗口立即更新按钮进程消息
ipcMain('quitAndInstall', () => {
NEED_INSTALL = false;
autoUpdater.quitAndInstall(true, true);
})
//用户点击稍后安装后程序退出时执行立即安装更新
app.on('will-quit', () => {
if (NEED_INSTALL) {
autoUpdater.quitAndInstall(true, true);
}
});
Чтобы вышеуказанный процесс выполнялся правильно, также необходимо предоставить правильный сервис для возврата правильной конфигурации обновления:
Укажите адрес услуги в коде
autoUpdater.setFeedURL({
provider: 'generic', //自定义服务器的选项,其他选项都不是自定义的
url: 'https://www.shaokaotan.com/autoUpdate/',
channel: 'myProgram',
});
Установка электронного обновления таким образом потребует адрес сетевой службы https://www.shaokaotan.com/autoUpdate/myProgram.yml, Этот адрес должен вернуть файл с информацией о конфигурации обновления, который упакован электронным сборщиком [канал .yml, этот канал настраивается в win.publish в конфигурации упаковки электронного компоновщика. Например, файл yml моей конфигурации упаковки выглядит так:
//winLatest_1.0.0.111.yml
version: 1.0.0-111
files:
- url: MyProgram1.0.0.111.exe
sha512: nRDCB1qk7GZ8DxUpH4wgHQ2zjr2n3kSrHhyJSXeOgx6akfYGEBp9/8qz8OLoos5q9+wKNR1LH0oelj70isN2tA==
size: 44897270
path: MyProgram1.0.0.111.exe
sha512: nRDCB1qk7GZ8DxUpH4wgHQ2zjr2n3kSrHhyJSXeOgx6akfYGEBp9/8qz8OLoos5q9+wKNR1LH0oelj70isN2tA==
releaseDate: '2020-07-27T07:28:08.168Z'
Следует обратить внимание на следующие три момента
- electronic-updater проверит формат версии в конфигурации, и он должен соответствовать спецификации версии semver (упакован позже), но обычное приложение Windows имеет 4-значный номер версии, поэтому его можно передать в форме из *.*.*-* теста
- electronic-updater проверит значение sha512 в конфигурации и значение sha512 загруженного файла.Если они не совпадают, будет выдана ошибка обновления, поэтому установщик не может быть изменен по желанию (до тех пор, пока значение md5 файла файл не изменился)
- Значением пути в конфигурации является адрес назначения electronic-updater для скачивания и обновления, а также адрес файла новой версии инсталляционного пакета программы (в эпоху преобладания cdn этим адресом должен быть файл адрес инсталляционного пакета, хранящегося на сервере cdn)
Конечно, невозможно напрямую вернуть yml-файл, запакованный электрон-билдером, в электрон-апдейтер.Вышеприведенный адрес инсталляционного пакета, очевидно, нуждается в перенастройке.Вы можете вручную создать yml-файл, соответствующий спецификации, после генерации версии. , или вы можете сделать это, как я:
//https://www.shaokaotan.com/autoUpdate/myProgram.yml
//该地址是一个正规的restful接口,不是文件地址
router.get('/autoUpdate/myProgram.yml', (req, res)=> {
//将electron-builder打包出来的yml文件上传到服务器保存成json配置,期间可以修改其中的值
let ymlConfig = config.updaterInfo;
//将响应头设置成文件附件
res.attachment('myProgram.yml');
//返回json形式的信息,json和yaml格式可以相互转换的
res.json(hugoAppConfig.updateVersionInfo);
});
Автоматические обновления не проблема!
Резюме этой статьи
Эта статья просто суммирует ключевые моменты ям, на которые я наступил.На самом деле, в электроне есть много мест, которые мы можем изучить, таких как плагины, разработка, оптимизация производительности и так далее. Передний киоск для барбекю продолжит делиться исследованиями, связанными с электронами, так что следите за обновлениями!
наконец
Обратите внимание на «Блуждание по фронтенду» и получайте качественные статьи как можно скорее.
В этой статье используетсяmdniceнабор текста