Electron 的打包与构建

4 年前
1062
2
这篇文章上次修改于 3 年前,可能部分内容已经不适用,如有疑问可询问作者。

说起 Electron,大家能定不会感觉到陌生,庞大的体积,内置浏览器,Hello World 都有 200+M... 我个人是很反感跨段应用的,虽然对于开发来说,节省了很多时间,但是站在用户的角度来讲,体验就不是那么称心如意了。但是最近一些业务需要用到 Electron,折腾过程中也踩了不少坑,总结一下。

开发环境的搭建

平时我们在开发前端应用时,一般都是使用 Webpack 去打包,在开发环境中,也是由 Webpack dev server 来实现 HMR。在 Electron 中也是可以使用 Webpack 的。

我们使用 electron-wepack 包,简单搭建一下环境。

yarn add source-map-support
yarn add -D electron electron-webpack electron-builder webpack

然后我们参考这个项目结构建立目录:

project/
├─ resources/
│  ├─ icon                         // 程序图标
├─ src/
│  ├─ main/                     // 主进程
│  │  └─ index.ts
│  ├─ renderer/             // 渲染层(启动界面)
│     └─ index.js
└─ static/                          // 静态资源

src 目录下的分别为存放 Electron 主进程逻辑(main) 和 渲染层(renderer)。入口文件必须为 indexmain

TypeScript 支持 (可选)

yarn add electron-webpack-ts typescript -D

安装完以上依赖,electron-webpack 会识别支持 TypeScript。

渲染层

src/renderer/index.ts 中,你可以操作 DOM 树。electron-wepack默认会提供一个空白的 HTML 文档,只有一个 #app 节点供你使用,你无法通过一般操作自定义一个入口 index.html, 但是你也可以用其他手段达到这个目标,在此不多赘述 (参看 issue)。

// src/renderer/index.ts
const $app = document.getElementById('app')!

$app.textContent = 'Hello World'

主进程

src/main/index.ts 中, 简单建立一个 app

import { app, BrowserWindow } from 'electron'
import { createWindow } from './common/window'

let mainWindow: BrowserWindow
app.on('ready', () => {
  mainWindow = createWindow()
  app.show()
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

其次是 window.ts,建立一个 window

import { BrowserWindow } from 'electron'
import path from 'path'
import { isDev } from '../utils'
import { format } from 'url'
export function createWindow() {
  const mainWindow = new BrowserWindow({
    height: 620,
    width: 400,
    webPreferences: { nodeIntegration: true }, // 一定要加!!!
  })
  if (isDev) {
    mainWindow.loadURL(
      `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`, // 开发环境
    )
  } else {
    mainWindow.loadURL(
      format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file',
        slashes: true,
      }),
    )
  }
  return mainWindow
}

脚本

package.json 中添加。

{
  "scripts": {
    "prebuild": "rm -rf dist",
    "build": "cross-env NODE_ENV=production electron-webpack",
    "start": "electron-webpack dev",
    "package": "yarn build && electron-builder build --publish never"
  }
}

执行 yarn start 。发现正确显示了 Hello World。

oihA1t

oihA1t

使用 yarn package 来生成 dmg 也是没有问题的。一般教程到此就结束了,但是我们的需求并不是这么简单,我们还需要配置其他,比如 app version,app icon,app sign key... 而这些配置也有很多坑。

配置

图标

应用图标需要不同大小的几张 png 以及 icns 等格式的图片,手动操作比较麻烦,我们可以用一张 png 去生成,使用 electron-icon-builder 工具就能轻松转换到我们想要的结果。

npx electron-icon-builder -i ./path-your-icon-file.png -o output
.
├── icon.icns
├── icon.ico
├── icon.png
└── icons
    ├── 1024x1024.png
    ├── 128x128.png
    ├── 16x16.png
    ├── 24x24.png
    ├── 256x256.png
    ├── 32x32.png
    ├── 48x48.png
    ├── 512x512.png
    └── 64x64.png

把生成的文件放入 resources 文件夹内,如不存在则新建。

jq8DsT

jq8DsT

package.json 中加入 build 字段,用于配置 electron-builder

{
  "build": {
    "appId": "com.innei.electron-template",
    "productName": "template",
    "extraMetadata": {
      "main": "main.js"            // **必须** 
    },
    "copyright": "Copyright © 2019-2020 ${author}",
    "mac": {
      "category": "public.app-category.utilities"
    },
    "files": [
      "package.json",
      "resources/**/*",                          // **必须** 
      "static",  
      {
        "from": "dist/main"           // **必须** 
      },    
      {
        "from": "dist/renderer"      // **必须** 
      }
    ],
    "extends": null,
    "dmg": {
      "contents": [
        {
          "x": 130,
          "y": 220
        },
        {
          "x": 410,
          "y": 220,
          "type": "link",
          "path": "/Applications"
        }
      ]
    },
    "win": {
      "icon": "resources/icon.ico",
      "target": [
        "nsis",
        "msi"
      ]
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true,
      "installerIcon": "resources/icon.ico"
    },
    "linux": {
      "target": [
        "deb",
        "rpm",
        "AppImage"
      ],
      "category": "Development"
    },
    "directories": {
      "buildResources": "resources",
      "output": "release"
    },
    "extraResources": [
      {
        "from": "resources/",  // **必须** 
        "to": "resources/"     // **必须** 
      },
      {
        "from": "static",
        "to": "static"
      }
    ]
  }
}

这里是个大坑,因为我们自定义了配置,覆盖了原来 electron-webpack 的配置,所以有几个地方是必须要这么写的,否则就会在打包之后无法显示 renderer 或者 找不到入口文件。这是我自己摸索出来的,比较 hack 的方法。因为我实在找不到答案。

如果你需要使用 __static 常量的话,

{ // 也是必须的
        "from": "static",
        "to": "static"
}

最后,附上 GitHub 地址:

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...