序
electron forge是electron官方推荐的打包、分发工具,怎么说呢,可以参考的资料少之又少,不过话又说回来,其他的打包工具可以参考的内容也并不见得就很多。
在正式切入主题以前,先谈谈electron项目的开发问题。当然,几乎不论你怎么搜索electron项目入门,互联网上可追寻的踪迹依然是使用electron-builder、electron-forge等等脚手架直接init的项目指南。若是指定要vue、react、angular任意一种前端开发语言,几乎清一色的都是使用对应的项目builder依赖,完了一看,webpack、vite版本不对,依赖不对,甚至electron的可用版本还停留在上个世纪。
所以本文的意义在于,一是谈谈现状,二是谈谈如何从零开始做一个electron项目。当然,打包工具基于electron forge,虽然实际使用后forge真的有很多令人操蛋的地方,但是相信掌握了这个逻辑以后即便是换成其他打包工具也不会存在问题。
项目结构
electron的项目从严格意义上来讲,分成main、preload、renderer 3块,其中main、renderer 2块内容相互独立,perload 存在与main和renderer互相的关联。
正常来讲,从开发顺序上来讲,一般是renderer(前端项目),其次才是main和preload。无论项目是不是过分依赖于node,都可以按照这个顺序来进行开发。需要使用node的地方,通过preload进行引入并提前做好声明(针对typescript开发)。
从脚手架初始化前端(renderer)项目
以vue项目为例,直接使用vue-cli进行项目初始化。当然react、angular也是一样的,本文不针对特定的前端开发框架。
本文使用vue 3、webpack 5、electron 17进行开发。
添加electron
本文在vue项目根目录单独建立了一个文件夹electron用于存放electron项目相关文件。主要也就是main.js、preload.js。
记得添加electron的npm依赖。
添加electron forge
添加electron forge依赖,并按照官方指南进行import。
编写electron forge配置文件
electron forge的配置文件是本文的重点。当然electron forge对于项目文件的操作逻辑也是很值得借鉴的,首先编译electron的文件,其次编译renderer(前端)的项目文件。
详细的内容就看下面代码中的注释吧。
const { WebpackPlugin } = require("@electron-forge/plugin-webpack");
const CopyPlugin = require("copy-webpack-plugin");
const path = require("path")
// 导入vue的webpack构建配置,按照vue.config的说明,需要从node_modules下导入
const rendererConfig = require("./node_modules/@vue/cli-service/webpack.config.js")
// 重定向vue项目的编译输出文件目录。之所以要输出到该目录是因为forge的main默认路径(且无法更改就是该路径)
rendererConfig.output.path = `${path.resolve(__dirname)}/.webpack/main`
// 重置入口文件,使用forge的entryPoints作为唯一入口配置
rendererConfig.entry = {}
// 移除vue plugins的CopyPlugin规则,因为默认会将public复制到dist,此处的输出文件已经不是dist而是.webpack/main
rendererConfig.plugins = rendererConfig.plugins.filter((v) => (!(v instanceof CopyPlugin)));
// 添加自定义的CopyPlugin规则,这个应该可以看懂,不需要额外的解释吧?
rendererConfig.plugins.push(
new CopyPlugin(
{
patterns: [
{
from: `${path.resolve(__dirname)}/public`,
to: `${path.resolve(__dirname)}/.webpack/main`,
toType: 'dir',
noErrorOnMissing: true,
globOptions: {
ignore: [
'**/.DS_Store',
'**/index.html',
]
},
info: {
minimized: true
}
},
{ // 必须!entryPoints的preload没啥用,需要单独执行复制操作。当然如果需要执行混淆,可以将preload的文件放到前端项目目录中,然后额外添加编译规则(使用对象指定entry)即可。具体的参考webpack的相关配置。
from: `${path.resolve(__dirname)}/electron/preload.js`,
to: `${path.resolve(__dirname)}/.webpack/main`
},
]
}
),
)
// 默认导出forge webpack的配置
module.exports = {
packagerConfig: { // electron-packager的配置参数,直接参考electron-packager官方说明即可
appBundleId: "com.terminal.client", // 自定义的app构建id,只允许用户运行一个app实例时很有用
name: "terminal-client", // 应用名称
out: ".webpack", // 需要打包的目录
junk: true, // 忽略系统的垃圾文件不将其复制到electron app中
asar: true, // 使用asar压缩资源目录
},
rebuildConfig: {}, // 一般不需要
makers: [ // makers参考forge官方说明即可,虽然我也有点晕,但是对结果影响不大
// {
// name: '@electron-forge/maker-squirrel',
// config: {},
// },
{
name: '@electron-forge/maker-zip',
platforms: ['darwin', "win32"],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [ // 重点!!!
new WebpackPlugin({
mainConfig: { // electron main的webpack打包配置
entry: "./electron/main.js", // 入口文件
module: {
rules: [ // 自己看应该可以看懂吧,这也是官方写法
// Add support for native node modules
{
// We're specifying native_modules in the test because the asset relocator loader generates a
// "fake" .node file which is really a cjs file.
test: /native_modules[/\\].+\.node$/,
use: 'node-loader',
},
{
test: /[/\\]node_modules[/\\].+\.(m?js|node)$/,
parser: { amd: false },
use: {
loader: '@vercel/webpack-asset-relocator-loader',
options: {
outputAssetBase: 'native_modules',
},
},
},
{
test: /\.tsx?$/,
exclude: /(node_modules|\.webpack)/,
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
},
]
},
resolve: { // 无需解释,webpack最基础的内容部分
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'],
},
output: { // 清除输出目录
clean: true,
}
},
renderer: { // 前端项目的webpack打包配置
config: rendererConfig, // 使用上述的rendererConfig配置
entryPoints: [
{
js: './src/main.ts', // 指定入口文件。官方同时使用了html指定网页模板,此处不需要,vue的webpack中有单独的Html插件处理Html。
name: 'main', // 名称,自己取就是了,app也行啥都行。
// 官方文档中此处还添加了preload,实际上来讲没有什么用,所以已经在上面的CopyPlugins中定义。preload的声明中甚至可以对preload进行额外的webpack打包操作,但是实际测试下来(也许是我没有操作对)也没有什么用,具体回到上面的Copyplugins看说明。
},
],
},
}),
]
};
结语
虽然内容些许简单,但是也是真的很简单。凡事都是一个思路,思路理解了什么事情都好办。