Из сообщества IMWeb, автор: лайнехен,Оригинальная ссылка
Мы знаем, что Electron предоставляет среду, подобную браузеру, с большим количеством разрешений для запуска наших веб-страниц, так как же Electron упаковывает код нашей веб-страницы в исполняемую программу?
В этой статье в основном рассказывается, как упаковывать приложения Electron и анализироватьelectron-builderКак упаковать наше приложение.
Как упаковать
В настоящее время у Electron есть два инструмента для упаковки:electron-userland/electron-builderа такжеelectron-userland/electron-packager.
Упаковка с электрон-строителем
Установите зависимости:
yarn add electron-builder --dev
// 或
npm i electron-builder --save-dev
Пакет:
- в проекте
package.jsonопределено в файлеname,description,versionа такжеauthorИнформация. - в проекте
package.jsonопределено в файлеbuildПоле:
"build": {
"appId": "your.id",
"mac": {
"category": "your.app.category.type"
}
}
- Добавить к
scriptsприбытьpackage.jsonсередина
"scripts": {
"pack": "electron-builder --dir",
"dist": "electron-builder"
}
- Пакет
Каталог пакета создается, но не упаковывается в виде файла
npm run pack
Создать файл exe или dmg
npm run dist
- Укажите платформу и архитектуру
# windows 64bit
electron-builder --win --x64
# windows and mac 32bit
electron-builder --win --mac --ia32
Технические характеристики:Command Line Interface (CLI)
Упаковка с электрон-упаковщиком
Установите зависимости:
npm i electron-packager --save-dev
Пакет:
electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]
Проще всего запустить напрямуюelectron-packager .Пакет.
по умолчанию,appnameдля текущего проектаpackage.jsonв файлеproductNameилиnameзначение поля;platformа такжеarchсогласуется с хозяином, вWindows 64位Следующий пакет — 64-битная версия Windows.
Конкретное значение каждого поля можно увидетьelectron-packager/usage.txt
Примечание:Приложения, которые упаковывают Windows под OS X, должны быть установленыWineпросто делать,electron-packagerНужно использоватьnode-rceditредактироватьElectron.exeдокумент.
Building an Electron app for the Windows target platform requires editing the Electron.exe file. Currently, Electron Packager uses node-rcedit to accomplish this. A Windows executable is bundled in that Node package and needs to be run in order for this functionality to work, so on non-Windows host platforms, Wine 1.6 or later needs to be installed. On OS X, it is installable via Homebrew.
анализ упаковки электронного построителя
анализ размера файла
Поскольку для достижения кросс-платформенной цели каждое приложение Electron содержит весь движок V8 и ядро Chromium, так что пустой проект Electron используетelectron-builder --dirПосле упаковки несжатая папка проекта достигла размера 121,1 МБ. При использованииelectron-builderДля упаковки размер установщика составляет 36 МБ, что приемлемо.
Но это пустой проект, так насколько большим будет фактический проект после упаковки? Проект с установленными 30+ зависимостями, размер папки проекта 230+ МБ до генерации инсталляционного пакета и 56,3 МБ после генерации инсталляционной программы.Более около 20МБ.
Но на самом деле 20Мб это не очень научно.В самом проекте нет больших файлов ресурсов.Если это просто код, то размер в распакованном виде должен быть меньше 10Мб. Так что же заставило размер проекта приблизиться к 100 МБ?
Пакетная структура проекта
Давайте посмотрим на пакетную структуру проекта (electron-builder --dir)
плюс--dirпараметр, чтобы просмотреть структуру каталогов приложения без упаковки всего приложения в установочный файл:
.
├── locales
│ ├── am.pak
│ └── ... 一堆的 pak 文件
├── resources
│ ├── app.asar (空项目只有 2KB,一个实际项目有 130MB+)
│ └── electron.asar (大小在 250KB 左右)
├── electron.exe (67.5MB)
└── ...
Многие файлы здесь игнорируются, мы в основном смотрим наelectron.exeдокументы иresourcesпапка. Поэтому реальный проект и пустой проект должны быть на app.asar.
app.asar
существуетdist/win-unpacked/resources/сгенерировано нижеapp.asarфайл, который представляет собой файл, сжатый asar. Мы можем разархивировать его и посмотреть, что внутри:
# 安装 asar
npm install -g asar
# 解压到 ./app 文件夹下
asar extarct app.asar ./app
Каталог распаковки выглядит следующим образом:
.
├── CHANGELOG.md
├── README.md
├── core
├── electron
├── icon
├── node_modules
├── package.json
├── test
├── view
└── webpack.config.js
Будет ли этот каталог выглядеть знакомо? ~ фактически упаковал содержимое всего нашего проекта. Да конечноnode_modulesПапки имеют специальную обработку, упакованы только здесьproduction dependencies, то есть вpackage.jsonизdependenciesОпределяется в зависимости.
Пустые проекты и разрыв в размерах между фактическим проектом зависят.
electron.asar
Давайте посмотрим, что упаковывает электрон.асар:
asar extract electron.asar ./electron
.
├── browser
│ ├── api
│ ├── chrome-extension.js
│ ├── desktop-capturer.js
│ ├── guest-view-manager.js
│ ├── guest-window-manager.js
│ ├── init.js
│ ├── objects-registry.js
│ └── rpc-server.js
├── common
│ ├── api
│ ├── atom-binding-setup.js
│ ├── init.js
│ ├── parse-features-string.js
│ └── reset-search-paths.js
├── renderer
│ ├── api
│ ├── chrome-api.js
│ ├── content-scripts-injector.js
│ ├── extensions
│ ├── init.js
│ ├── inspector.js
│ ├── override.js
│ ├── web-view
│ └── window-setup.js
└── worker
└── init.js
Исходный код, связанный с Electron, сжат в файл electronic.asar.
Анализ упаковки
Вывод информации электронщиком при упаковке
При упаковке мы видим, что консоль выводит следующую информацию:
• electron-builder version=20.15.1
• loaded configuration file=package.json ("build" field)
• writing effective config file=dist/electron-builder-effective-config.yaml
• rebuilding native production dependencies platform=win32 arch=x64
• packaging platform=win32 arch=x64 electron=1.8.7 appOutDir=dist/win-unpacked
Если вы также хотите упаковать программу, есть также следующая информация для печати:
• building target=nsis file=dist/xxx.exe archs=x64 oneClick=true
• building block map blockMapFile=dist/xxx.exe.blockmap
Можно примерно сказать, что упаковка в основном выполняет следующие функции:
- переустановить зависимости
- Пакет
Информация, которую я знаю отсюда, по-прежнему относительно ограничена, поэтому мне все еще нужно просмотреть входные данные изelectron-builderЧто произошло в середине генерации файла installer.
"bin"
мы установили изelectron-builderзависимыйpackager.jsonИнформация о поле «bin» определения файла может видеть, что он выполняется./out/cli/cli.jsэтот файл.
"bin": {
"electron-builder": "./out/cli/cli.js",
"build": "./out/cli/cli.js",
"install-app-deps": "./out/cli/install-app-deps.js"
}
./outФайлы в каталоге былиbabelПосле перевода мы можем перейти к загрузкеelectron-builderисходный код для анализа.
"packages/electron-builder/src/cli/cli.ts"
Из исходного кода нетрудно найтиpackages/electron-builder/src/cli/cli.tsЭтот файл является файлом ввода для команды. Анализ от входного файла вниз:
packages/electron-builder/src/builder.ts
cli.tsФайл, который импортирует предыдущий каталогbuilder.tsэкспорт файлаbuildметод.buildметод создаетPackagerобъект, а затем вызватьpackages/electron-builder-libэкспортируетсяbuildметод.
cli.tsсерединаbuildметод:
export function build(rawOptions?: CliOptions): Promise<Array<string>> {
const buildOptions = normalizeOptions(rawOptions || {})
const packager = new Packager(buildOptions)
let electronDownloader: any = null
packager.electronDownloader = options => {
if (electronDownloader == null) {
electronDownloader = BluebirdPromise.promisify(require("electron-download-tf"))
}
return electronDownloader(options)
}
return _build(buildOptions, packager)
}
packages/electron-builder-lib/index.ts
export async function build(options: PackagerOptions & PublishOptions, packager: Packager = new Packager(options)): Promise<Array<string>> {
...
return await executeFinally(packager.build().then(() => Array.from(artifactPaths)), errorOccurred => {
...
})
}
buildметод называетсяpackagerизbuildметод.
packages/electron-builder-lib/packager.ts
Метод сборки вызывается после обработки некоторой информации_buildметод:
async build(): Promise<BuildResult> {
...
return await this._build(configuration, this._metadata, this._devMetadata)
}
_buildМетод продолжает вызывать закрытый методdoBuild:
async _build(configuration: Configuration, metadata: Metadata, devMetadata: Metadata | null, repositoryInfo?: SourceRepositoryInfo): Promise<BuildResult> {
...
return {
outDir,
platformToTargets: await executeFinally(this.doBuild(outDir), async () => {
if (this.debugLogger.enabled) {
await this.debugLogger.save(path.join(outDir, "electron-builder-debug.yml"))
}
await this.tempDirManager.cleanup()
}),
}
}
doBuildОн отвечает за то, какие установочные пакеты платформы создавать и как их упаковывать:
private async doBuild(outDir: string): Promise<Map<Platform, Map<string, Target>>> {
...
for (const [platform, archToType] of this.options.targets!) {
const packager = this.createHelper(platform)
for (const [arch, targetNames] of computeArchToTargetNamesMap(archToType, packager.platformSpecificBuildOptions, platform)) {
await this.installAppDependencies(platform, arch)
const targetList = createTargets(nameToTarget, targetNames.length === 0 ? packager.defaultTarget : targetNames, outDir, packager)
await createOutDirIfNeed(targetList, createdOutDirs)
await packager.pack(outDir, arch, targetList, taskManager)
}
}
return platformToTarget
}
createHelperФактически в его основе лежит платформа для создания соответствующихPackagerобъект и установить зависимости приложений в соответствии с различными архитектурами и, наконец, вызватьpackспособ упаковки.
Более поздний анализ упаковки платформы WindowsWinPackager
WinPackager
ФактическиWinPackagerунаследовано отPlatformPackagerДобрый,packМетод также определен в этом родительском классе:
async pack(outDir: string, arch: Arch, targets: Array<Target>, taskManager: AsyncTaskManager): Promise<any> {
const appOutDir = this.computeAppOutDir(outDir, arch)
await this.doPack(outDir, appOutDir, this.platform.nodeName, arch, this.platformSpecificBuildOptions, targets)
this.packageInDistributableFormat(appOutDir, arch, targets, taskManager)
}
Этот метод вызывает другой методdoPack:
protected async doPack(outDir: string, appOutDir: string, platformName: string, arch: Arch, platformSpecificBuildOptions: DC, targets: Array<Target>) {
...
const computeParsedPatterns = (patterns: Array<FileMatcher> | null) => {
if (patterns != null) {
for (const pattern of patterns) {
pattern.computeParsedPatterns(excludePatterns, this.info.projectDir)
}
}
}
const getFileMatchersOptions: GetFileMatchersOptions = {
macroExpander,
customBuildOptions: platformSpecificBuildOptions,
outDir,
}
const extraResourceMatchers = this.getExtraFileMatchers(true, appOutDir, getFileMatchersOptions)
computeParsedPatterns(extraResourceMatchers)
const extraFileMatchers = this.getExtraFileMatchers(false, appOutDir, getFileMatchersOptions)
computeParsedPatterns(extraFileMatchers)
const packContext: AfterPackContext = {
appOutDir, outDir, arch, targets,
packager: this,
electronPlatformName: platformName,
}
const taskManager = new AsyncTaskManager(this.info.cancellationToken)
const asarOptions = await this.computeAsarOptions(platformSpecificBuildOptions)
const resourcesPath = this.platform === Platform.MAC ? path.join(appOutDir, framework.distMacOsAppName, "Contents", "Resources") : (isElectronBased(framework) ? path.join(appOutDir, "resources") : appOutDir)
this.copyAppFiles(taskManager, asarOptions, resourcesPath, path.join(resourcesPath, "app"), outDir, platformSpecificBuildOptions, excludePatterns, macroExpander)
await taskManager.awaitTasks()
const beforeCopyExtraFiles = this.info.framework.beforeCopyExtraFiles
if (beforeCopyExtraFiles != null) {
await beforeCopyExtraFiles(this, appOutDir, asarOptions == null ? null : await computeData(resourcesPath, asarOptions.externalAllowed ? {externalAllowed: true} : null))
}
await BluebirdPromise.each([extraResourceMatchers, extraFileMatchers], it => copyFiles(it))
await this.info.afterPack(packContext)
await this.sanityCheckPackage(appOutDir, asarOptions != null)
await this.signApp(packContext)
await this.info.afterSign(packContext)
}
Здесь мы знаем,app.asarФайл создается этим методом.
При упаковке, это черезMatcherЧтобы добиться выборочной упаковки файлов. отFileMatcherСоответствующие определения можно увидеть в:
export const excludedNames = ".git,.hg,.svn,CVS,RCS,SCCS," +
"__pycache__,.DS_Store,thumbs.db,.gitignore,.gitkeep,.gitattributes,.npmignore," +
".idea,.vs,.flowconfig,.jshintrc,.eslintrc,.circleci," +
".yarn-integrity,.yarn-metadata.json,yarn-error.log,yarn.lock,package-lock.json,npm-debug.log," +
"appveyor.yml,.travis.yml,circle.yml,.nyc_output"
export const excludedExts = "iml,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,suo,xproj,cc,d.ts"
electron.exe
мы бежимelectron.exeИсполняемые программы на самом деле являются уже скомпилированными файлами. Его функция заключается в загрузкеresources/app.asarСодержимое файла, включая местоположение файла записи, такжеapp.asarупаковано вpackage.jsonизmainполе для загрузки.
Все, что нужно сделать упаковщику, это просто поместить этоelectron.exeВы можете изменить значок, автора, версию и другую информацию о файле.
Суммировать
Вышеупомянутое простоelectron-builderПроцесс упаковки был проанализирован. Благодаря анализу мы узнали:
- Распределение объема приложений Electron:
electron.exeОколо 67,5 МБ, Electron.Asar Около 250 КБ, App.asar Project основана на фактической разнице, будет относительно большой, пустой проект составляет около 2 КБ, тестирование в реальном проекте составляет около 130 МБ. Приложение. Сасар Большая причина, которая будет больше зависеть от фактического проекта, а инструменты упаковки требуются при упаковке всегоnode_modulesПапки все запакованы, поэтому объем будет намного больше.
- как появился исполняемый файл
Реализуя универсальную исполняемую программу, эта программа делает следующее:resources/app.asarВ качестве корневого каталога проекта запуститеapp.asar/package.jsonсерединаmainУказывает файл в качестве файла записи. Различные приложения должны быть только соответствующим образом переупакованыapp.asarВот и все. Наконец, мы можем получить наше приложение, изменив значок и другую информацию этой исполняемой программы~
- Возможные проблемы с упаковкой
electron-builderХотя упаковка помогает нам отфильтровать некоторые файлы и не упаковывать их, исходный код нашего проекта упакован без какой-либо обработки.
【Использованная литература】
PS: Для оптимизации упаковки Electron вы можете обратиться к другой статье автора.«Оптимизация электронной упаковки — с 393 МБ до 161 МБ»