Electron+Vue构建单体项目打包细节(包含ARM平台踩坑经验)

Electron+Vue构建单体项目

Electron:使用JS、HTML和CSS搭建跨平台的桌面应用程序的工具。核心就是跨平台和前端技术,不需要后台(java、php等技术)。

​        Electron实现这个的基础是它集成了一个浏览器内核,不需要依赖于客户端的其它应用,直接和客户端的CPU、操作系统相关联。就是说它提供了一套适配于各个平台的底层平台,从而达到这个效果。

Vue:渐进式JavaScript框架。以前都是叫MVVM响应式前端框架,现在换了一个叫法。从意义上去理解的话就是Vue提供一套核心库,然后使用者根据自身项目的情况去逐渐的增加相应的插件。是不是感觉和JAVA很相似,提供一套语法,然后根据情况去封装功能包。

​        Vue其本身也是JS、HTML和CSS,只是它提供了一套引擎来帮助实现MVVM(Model,模型层 ;View,视图层;ViewModel,V与M连接的桥梁,也可以看作为控制器)的功能。简单来说就是数据实时绑定和刷新。

​        因此Electron和Vue联合使用是完全没有问题的。因此很早以前就有人将Vue嵌入到Electron中,称之为Electron-Vue,这个构建过程网上教程很多,一看就懂的那种"hello word"。

依赖的插件下载缓慢或者直接报错

  1. 基本上第一步都是切换到TB镜像上,命令:npm config set registry https://registry.npm.taobao.org
  2. 第一步能解决大部分的情况,但是Electron比较特殊,切换后仍然不太稳定,需要单独指定Electron的镜像,命令:npm config set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron/,设置后windows环境下一般都可以下载了
  3. 打包的时候偶尔会报下载失败,因为node打包的时候不是100%取本地已经下载的文件,会根据一定的规则(网上说是判断是否是node本身的包会直接走本地)去对应的网站上下载已经打好的对应环境的dist包,如果没有则下载源码编译,所以必要的时候可以设置dist源也为TB,命令:npm config set disturl https://npm.taobao.org/dist
  4. 如果有梯子,以上都不用操作,直接用梯子下载就好了。

ARM架构下的打包

​        这个因为有一些“前人”的探索,所以网上还是有一些资料的,但是因为前端项目的文档和使用说明太过于简略或者根本只有看源码才知道有这个东西的特性,还是会有非常多不确定因素。

  1. 因为ARM架构属于市面上很少见的机型(国产化),因此很多编译好的插件包基本没有,这就需要打包的时候重新对源码在平台下进行编译。由于很多源码它本身写的年代比较久了或者说是近几年的发展太快了,很多时候会出现编译通不过。因此需要更改环境变量以适配编译的依赖。经过实操,发现在node14版本基本都可以编译通过,在node16版本会出现各种变量不存在、依赖不存在的报错,这是由于使用的编译库不兼容,因此在非X86下,建议使用node14的版本进行打包。

  2. 在编译过程中遇到一个很扯淡的问题,Electron在打包的时候采用的是Electron-builder的插件进行的,这个插件内部是使用ruby来进行打包和操作的。前面说过Electron是支持跨平台的,但是在ARM架构下,Electron提供的是linux-x86的Electron-builder,二进制不一样导致没办法编译。打包就报错:可执行文件格式错误。定位到文件是Electron-builder自带的fpm下的ruby环境是x86的。最开始以为是包下错了,结果把日志打印出来后去下载的地址和github源码上确实没有ARM架构的该编译包(同时找到了electron-builder是放到用户的缓存文件夹里面的,这给了一定的操作控件);奈何这个fpm的关键词搜索都是怎么安装fpm然后怎么打包之类的完全没有如何处理环境的问题。

1. 想着是ruby的问题,就去找ARM版本ruby的安装包,安装后发现和缓存里面ruby下面的文件基本一模一样,然后直接把这些文件拷过去,再次打包果然报错信息变成了ruby依赖的一个包没有,这下对于我这个前端小白来说就直接蒙了。
   2. 回过头来,那是不是直接按照fpm的安装和打包命令打一个包就可以了,因此按照网上的操作(谢天谢地yum里面有fpm)把fpm安装上,然后尝试打包,中间经历了一些波折,最后打出来了一个deb的包,然后安装倒是没有问题,但是不是一个可执行的应用,就是解压出了一些文件,因此怀疑Electron-builder除了这个应该还有什么操作是我们遗漏的,但是目前来说这个我们是没法去深入处理的。
   3. 又开始在搜索引擎中开始查找相关的信息,终于在github的一次讨论中找到一个配置,设置环境变量USE_SYSTEM_FPM="true",设置了就使用本机的fpm去打包,感觉这个靠谱,尝试了一下,通过了,打出来的包也是一切正常的。

  1. package.json的一些细节配置:

在linux下author必须带有url或者邮箱,可以简写为:“author”:“xxxxx [email protected]”这个格式,具体的可以参见网上electron-builder的package.json的配置说明(https://www.electron.build/configuration/linux);package.json中的scripts是npm打包时的运行命令,其中在使用electron-builder打包命令的时候,可以指定平台参数,例如:

1. windows平台:electron-builder --win --ia32;指定以windows的32位版本来打包,默认是–x64即64位
   2. linux平台:electron-builder -l --arm64;指定以linux的ARM64来打包,还有一个是–armv7l ,以32位的ARM来打包,这其中还有一个知识点,还有一个armv8的架构,因为不涉及使用就没深入研究

在打包的build配置项中,ARM最好设置"asar":false,不把编译中的asar包打出来,然后在build里面的linux配置中,使用"packageCategory":"Utility"来指定打包的压缩方式为Utility(Small utility application, “Accessories”);建议使用deb的打包格式,网上有个说使用appimage格式会有环境依赖问题。

Electron打开多个子窗口

​        目前大部分的前端页面都是采用modal的方式在页面内进行打开的,但是有些页面要填的数据量巨大而且逻辑过于复杂的情况下,就使用的是window.open的方式新开页签。Electron支持window.open的方式新开一个窗口,但是有且仅能有一个子窗口,如果子窗口存在的情况下又调用open新开子窗口Electron会直接将现在打开的窗口刷新为新的open函数指定的内容。因此如何open多开子窗口,子窗口和父窗口之间的通信是electron中比较特殊的存在。

​        要在electron中多开子窗口,网上也很好找资料,就是remote模块下的BrowserWindow对象,这个对象的参数设置基本和主程序的BrowserWindow(https://www.electronjs.org/docs/api/browser-window)一模一样,只需要简单的设置好参数然后loadUrl就好了。

ps:要注意的是remote窗口默认设置的是直接引入外部的网页而不是electron内的网页,因此如果你没有设置nodeIntegration参数的话,直接打开electron内的网页会直接报错,没有node的环境。

let childWinodw = new remote.BrowserWindow({
  height: 900,
  useContentSize: true,
  autoHideMenuBar: true,
  width: 1440,
  frame: true,
  webPreferences: {
    nodeIntegration: true,
    contextIsolation: false,
    enableRemoteModule: true
  }
})
childWinodw.loadURL(_url)

Electron父子窗口通信

​        因为每个子窗口的打开是完全独立的(就是无法从window没有opener,控制台打出来直接是null),这个我也没有弄的很清楚是为什么(猜测是安全性相关的限制)。但是子窗口一般都是编辑、新增之类的和父窗口有一定关系的,最简单的需求就是新增后要刷新父窗口的列表信息。在这块无法通过本身的window对象定位父页面,那么就没办法按照常规的window.opener.postMessage传递跨页面的消息。网上搜索也没有什么进展,这个问题一度卡主。

​        经过各种实践发现remote.BrowserWindow有个方法getAllWindows可以获取全部的已经创建的对象数组,每个对象都有一个唯一的id,然后每次新开的都会在数组的前面,即数组的最后一个就是最原始的主窗口(父页面),因此利用这一点进行数据交互。通过getAllWindows拿到父窗口,然后这个地方只能使用electron独有的send来进行数据交互(机制和postMessage一样)。

ps:需要注意的是,send不能发送对象,只能发送字符串

remote.BrowserWindow.getAllWindows()[remote.BrowserWindow.getAllWindows().length - 1].webContents.send('topic', 'reloadList')

Electron父子窗口关联

​        最开始为了绑定父子窗口,设置了remote.BrowserWindow的parent属性,但是在实际打开的子窗口中还是拿不到父窗口,这个地方当时觉得也不影响就没有去掉。在实际使用过程中发现有时候关闭了一个子窗口导致所有的子窗口全部被关掉,观察发现:关闭前一个窗口会导致后面打开的窗口被关掉,但是关闭新打开的窗口不会有这个问题。因此猜测可能是窗口打开有关联性,回到配置把parent参数直接去掉,这个问题就解决了。因此parent这个参数绑定了子窗口和父窗口的生命周期。

你可能感兴趣的:(前端,node.js,vue.js,electron,前端,linux)