如何使用rollup打包编译electron主进程代码

同学们可以私信我加入学习群!

作者是个狭隘的人,最近通读新中国七十年经济、军事之起伏,深感其中艰辛无奈与愤慨,故无民族之心,无爱国之心,无友爱之心,请勿参考代码。

他人笑我过于单纯,我笑他人满身精明。匹夫之身,或无青云之志,唯所立三尺之地,尽是拳拳赤心,位卑未敢忘忧国


@TOC


前言

如果跟着本系列文章一路学习,到现在应该已经可以使用electron+vue开发项目。但是这样开发的项目,打包后electron主进程部分是以源码的形式存在的,这样有利于前期的技术研究,梳理清楚打包后各文件的关系等等。

但是,真正的项目中,这样的代码太容易被人拿去抄袭。所以需要借助rollup+terser,对源码进行压缩混淆等操作。

不过这些开源工具压缩后的代码,也只是增加了逆向的复杂度,如果需要更安全的代码保护,请借助专业付费的安全软件。


一、为什么选择rollup

如果能重来,我会选择webpack。但是,项目创建之处,为了体验vue3+vite,舍弃了webpack,这条技术栈也只能延续下来了。vite是基于rollup的,所以项目中使用rollup打包编译electron主进程部分,并不需要引入新的插件。

二、为什么不在vite中使用rollup插件

按照笔者的习惯,能在一个package.json中实现打包,就不为electron单独写一个打包配置,能用一个脚本vite.config.js完成编译,自然也就不想多写一个js脚本。

但是试验了几遍,做不到啊,不知道怎么在一个配置文件里,定义很多入口和出口文件的同时,为他们分别配置不同的参数。

归根结底,还是对vite和rollup熟练度不够,不过遇到困难,我选择放过自己,多写个rollup的配置来专门打包electron主进程部分。

所以现在项目的整体架构就是:

  1. package.json还是只有一个,包含vue3+electron项目信息、electron-builder打包插件的所有配置。

  2. vite.config.js配置vue3打包编译的参数。

  3. rollup.config.main.js配置主进程打包编译的参数

  4. rollup.config.preload.js配置渲染进程打包编译的参数。(这部分理论上可以和上面的rollup参数合并,我会分开它俩,纯粹为了简单)

  5. build.js是打包编译的脚本,里面根据生产、开发环境,修改package.json中的参数,调用rollup插件打包编译electron主进程代码。

三、package.json命令讲解

json 复制代码
"dev": "node ./public/build.js dev && npm-run-all --parallel dev:vue start",
    "build": "npm run build:vue && node ./public/build.js build &&  npm run build:e"
  • dev:因为生产和开发环境的入口文件是不一样的,需要通过build.js脚本修改package.json文件,所以dev命令中,在运行vue(dev:vue)、运行electron(start)之前,需要先运行我们的脚本build.js,并传递参数"dev",判断当前环境,将一些前置的条件修改好。 步骤:运行脚本build.js------运行vue------运行electron
  • build:与dev略有不同的是,为了目录简洁,我们把electron主进程编译后的代码打包到vue编译目录dist中,所以这就需要先存在dist目录,才能运行rollup插件打包electron主进程,这就是build命令中,要先打包vue(build:vue),再运行build.js脚本,并传递参数"build",最后运行electron-builder将所有代码打包。 步骤:编译vue------运行脚本build.js------打包electron

四、build.js揭秘

通过上面命令行,可以发现,除了build.js,其它都是很常规的前端命令。

不难猜测,如何修改package.json使其能够适配生产、开发环境,如何调用rollup插件打包编译electron主进程,这些操作都在build.js中。

build.js全部代码:

ini 复制代码
const path =require("path") ;
const fsPromise = require("node:fs/promises");
const fs = require("fs");
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);

(async function (){
    /*修改package.json*/
    const cmdConfig=process.argv.splice(2)[0]
    const url = path.join(process.cwd(), 'package.json')
    const json = fs.readFileSync(url, 'utf-8')
    let pkg = JSON.parse(json)
    if(cmdConfig=='dev'){
        // pkg.main='./electron/main/main.js'
        pkg.main='./electron/main/main.js'
        pkg.build.nsis.include= "./public/electron/installer.nsh"
        pkg.build.win.icon="./public/electron/favicon.ico"
    }else{
        pkg.main='./dist/assets/progress.js'
        pkg.build.nsis.include= "./dist/electron/installer.nsh"
        pkg.build.win.icon="./dist/electron/favicon.ico"

    }
    const pkgStr=JSON.stringify(pkg, null, 2)
    const writePromise=fsPromise.writeFile(url,pkgStr)
    await writePromise

    /*运行rollup*/
    if(cmdConfig=='build'){
        const { stdout, stderr } = await exec(`rollup --config rollup.config.main.js`);
        const { stdoutPreload, stderrPreload } = await exec('rollup --config rollup.config.preload.js');
        console.log('stdout:', stdout);
        console.log('stdoutPreload:', stdoutPreload);
        console.error('stderr:', stderr);
    }

})()

了解这部分代码需要入门的nodejs知识,不过没有相关知识的同学也不用焦虑,nodejs无非也就是提供了一些api和js运行环境,本质上还是js语言。

即使零基础,要理解上面的代码,也不难。如果基础扎实的同学,参考上面代码即可,可不用详细了解下面的解析。

  1. 为什么要写一个自执行函数,因为里面存在异步操作,写自执行是为了方便直接写async语法糖,没什么特殊考虑。把这部分拆开,先定义方法,然后再调用执行,也可以。
  2. const cmdConfig=process.argv.splice(2)[0]:process.argv是node提供的对象,里面保存着运行脚本时传递的参数,通过这句,我们就能获取到package.json命令行中定义的参数"dev"和"build",从而判断当前环境。
  3. const url = path.join(process.cwd(), 'package.json') const json = fs.readFileSync(url, 'utf-8') 这两句是在获取package.json的地址并读取其中的文本。读取后的文本为字符串格式。
  4. let pkg = JSON.parse(json):将读取的文本格式化为json,方便操作。
  5. if(cmdConfig=='dev'){}else{}:这部分是在判断当前环境后,修改package.json中的参数,因为源码形式中,package.json与electron入口文件main.js、nsh脚本的相对路径,主进程与图片等静态资源的相对路径都有可能不同,所以需要根据不同环境,修改对应的参数。
  6. const pkgStr=JSON.stringify(pkg, null, 2) const writePromise=fsPromise.writeFile(url,pkgStr) await writePromise 这三行代码是把修改后的json先变成字符串,然后写入package.json中,nodejs提供了多种形式的读写文件方式,如果不习惯这种promise方式,也可以改为其它形式。
  7. 运行rollup插件的核心就是:rollup --config rollup.config.js,这句话的意思是根据配置文件rollup.config.js运行rollup插件,因为我们需要分别编译主进程main.js和渲染进程preload.js,所以需要写两句代码。至于为什么要分别打包编译两部分,感兴趣的同学可以自行研究,这里不再赘述。

五、rollup配置揭秘

上面的打包脚本已经讲解地很细致了,即使同学对node不熟悉,也能了解每行代码在做什么。现在整套方案唯一还未讲解的,只有rollup配置了。

在build.js脚本中,只用两行代码,执行了rollup插件打包编译electron,而插件的参数,则配置在两个文件中:rollup.config.main.js、rollup.config.preload.js

以其中一个为例:

javascript 复制代码
import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
const mainConfig={
    input: 'electron/main/main.js', // 主进程入口文件
    output: {
        file: 'dist/assets/progress.js', // 输出文件
        format: 'cjs' // 使用 CommonJS 格式
    },
    plugins: [
        nodeResolve({
            extensions: ['.js'] // 只处理.js和.json文件
        }), // 解析 Node.js 模块
        commonjs(), // 转换 CommonJS 模块
        terser()
    ],
    external:[
        "electron"
    ]

}
export default mainConfig

这是main.js的rollup配置,主要是用了两个插件@rollup/plugin-commonjs和@rollup/plugin-node-resolve,terser为压缩混淆插件,可选用。

配置文件里,需要自己定义的就是入口文件,输出文件。从这里也可以看出,最终输出目录都定义为dist,所以会造成main.js与preload.js相对路径,在开发和生产环境有所不同,这也是我们为什么需要修改pacakge.js参数的原因。


总结

项目源代码:码云地址,或直接在码云搜索:李泽/electron-vue-basic。有问题随时私信博主,看到就会解答的,如果一直不理你,那就是我也不懂。

相关推荐
MR·Feng5 小时前
使用Electron将vue2项目打包为桌面exe安装包
前端·javascript·electron
weiabc18 小时前
学习electron
javascript·学习·electron
咖喱鱼蛋1 天前
Ubuntu安装Electron环境
linux·ubuntu·electron
sinat_384241091 天前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
Lei_zhen961 天前
记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题
前端·javascript·electron
辣条小哥哥1 天前
electron主进程和渲染进程之间的通信
javascript·electron·ecmascript
咖喱鱼蛋1 天前
Electron一些概念理解
前端·javascript·electron
sun lover2 天前
electron快速上手
javascript·electron
Jornici4 天前
搭建vue-electron项目
前端·vue.js·electron
横冲直撞de5 天前
electron客户端预览doc、docx、excel、pdf、ppt、csv、txt等文件类型
electron·pdf·excel