同学们可以私信我加入学习群!
作者是个狭隘的人,最近通读新中国七十年经济、军事之起伏,深感其中艰辛无奈与愤慨,故无民族之心,无爱国之心,无友爱之心,请勿参考代码。
他人笑我过于单纯,我笑他人满身精明。匹夫之身,或无青云之志,唯所立三尺之地,尽是拳拳赤心,位卑未敢忘忧国
@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主进程部分。
所以现在项目的整体架构就是:
-
package.json还是只有一个,包含vue3+electron项目信息、electron-builder打包插件的所有配置。
-
vite.config.js配置vue3打包编译的参数。
-
rollup.config.main.js配置主进程打包编译的参数
-
rollup.config.preload.js配置渲染进程打包编译的参数。(这部分理论上可以和上面的rollup参数合并,我会分开它俩,纯粹为了简单)
-
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语言。
即使零基础,要理解上面的代码,也不难。如果基础扎实的同学,参考上面代码即可,可不用详细了解下面的解析。
- 为什么要写一个自执行函数,因为里面存在异步操作,写自执行是为了方便直接写async语法糖,没什么特殊考虑。把这部分拆开,先定义方法,然后再调用执行,也可以。
- const cmdConfig=process.argv.splice(2)[0]:process.argv是node提供的对象,里面保存着运行脚本时传递的参数,通过这句,我们就能获取到package.json命令行中定义的参数"dev"和"build",从而判断当前环境。
- const url = path.join(process.cwd(), 'package.json') const json = fs.readFileSync(url, 'utf-8') 这两句是在获取package.json的地址并读取其中的文本。读取后的文本为字符串格式。
- let pkg = JSON.parse(json):将读取的文本格式化为json,方便操作。
- if(cmdConfig=='dev'){}else{}:这部分是在判断当前环境后,修改package.json中的参数,因为源码形式中,package.json与electron入口文件main.js、nsh脚本的相对路径,主进程与图片等静态资源的相对路径都有可能不同,所以需要根据不同环境,修改对应的参数。
- const pkgStr=JSON.stringify(pkg, null, 2) const writePromise=fsPromise.writeFile(url,pkgStr) await writePromise 这三行代码是把修改后的json先变成字符串,然后写入package.json中,nodejs提供了多种形式的读写文件方式,如果不习惯这种promise方式,也可以改为其它形式。
- 运行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。有问题随时私信博主,看到就会解答的,如果一直不理你,那就是我也不懂。