前言
近期工作上收到开发需求,公司欲大肆出售运行在特殊场景下的服务器硬件(基于Linux操作系统),前端工程师需要配合开发Linux系统下的图形化应用程序一起交付.
最终需要的应用程序通过调用硬件工程师编写的shell脚本直接获取机器当前运算数据和相关参数,然后渲染界面提供给用户查看.整个过程由前端与硬件工程师对接,不需要后端参与.
由于之前拥有使用Electron在windows下开发客户端软件的经验,这一次仍然选择Electron作为技术方案实现开发目标.
Electron是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,它内部集成了Chromium 和 Node.js,这也使得常见的浏览器具备了调用操作系统相关服务的能力.另外它最大的优势是跨平台,能够开发支持Windows、Mac和Linux系统下的客户端应用.
前端工程师一般对Linux操作系统接触较少,如何以最小的学习成本快速熟悉系统特点以及完成开发目标都是不小的挑战.整个开发流程先粗略归纳为以下几步.
- 搭建
Linux操作系统和图形化桌面 - 搭建
Electron项目架构,根据UI设计图完成静态页面的开发 - 静态页面完成后,编写调用
shell脚本的函数,通过调用脚本拿到机器数据,再渲染到页面上 - 项目开发完毕,打包成安装程序交付
安装Linux开发环境
Linux的环境搭建非常繁琐,很多人都会选择在本地安装虚拟机和操作系统,再对环境做一些设置.为了前期能够快速并且简单的用上Linux系统,可以考虑在阿里云上花几十块钱买一台短期并且安装完备的服务器.
首先登录阿里云官网注册一个账户,然后在产品栏的下拉列表里选择轻量应用服务器(如下图);

页面进入后选择配置,应用镜像那一栏Node.js选择14.15.2版本(如下图).如果Node版本选的太低,很多软件都跑不起来.

系统镜像那一栏选择Ubuntu 16.04版本(如下图).操作系统的镜像可以根据实际需求选择,目前Linux使用较多的操作系统是CentOs和Ubuntu.

配置过程很简单,到这里基本就结束了,然后点击确认按钮付钱就能获得一台服务器了.
付款成功后点击进入管理控制台,页面上展示了自己购买的服务器的相关参数,比如公网的IP地址,CPU使用情况以及内存占用大小.
服务器信息那一栏的右侧有一个重置密码,点击设置新密码后重启应用.
重启后点击右上侧的远程连接(如下图),在黑窗口里输入node -v可以查看node版本.这个黑窗口是一个命令行工具,可以在里面输入各种命令完成系统相关操作.

命令行继续输入sudo apt-get install git,在我们的服务器上安装一个git.这句安装命令中sudo是为了以管理员权限来运行.apt-get是一个包管理工具,我们可以使用它进行软件的下载、更新、卸载等操作,类似于前端使用的npm.
安装git的过程中,命令行可能会弹出Is this ok [y/N]:的询问,输入y继续安装.安装完毕后在命令行输入git --version就可以查看安装好的git版本.
安装图形化桌面
我们的目标是为了在Linux下开发一款图形界面程序,上面只有一个黑窗口的系统是满足不了需求的.我们还需要在Linux系统再安装图形化桌面,就像Windows系统一样,这样我们开发出来的程序就能在图形化桌面上展现出来.
-
更新下载源,在命令行输入
sudo apt-get update执行. -
安装桌面环境
xfce,输入命令sudo apt install xfce4执行.
VNC的配置
配置VNC是为了让本地的windows电脑能以图形化桌面的形式打开远程的服务器.
在阿里云服务器命令行输入sudo apt-get install vnc4server安装VNC.
VNC服务器安装完毕后,执行vncserver :1命令启动VNC(如果出现报错没有权限,命令前面加sudo再执行).这里第一次启动会出现输入连接密码,密码一定要记住,等下在本地windows连接远程的桌面时需要用到这个密码.
密码设置完毕后,开始修改VNC配置文件.先输入vncserver -kill :1命令,这句命令是为了防止修改文件时报错.
然后输入vi ~/.vnc/xstartup,打开VNC启动配置文件进入vi编辑器模式.
这里有学过Linux基础的同学应该知道,vi编辑器输入和退出有一套自己的规则.默认打开的情况下,你在键盘上输入字符并不会出现在文件上的,只有先输入i进入插入模式才能让输入的字符生效.
键盘输入i进入插入模式,随后在x-window-manager &那一行的前面输入#注释,再在下面粘贴以下配置信息(最终结果如下图所示).
sesion-manager & xfdesktop & xfce4-panel &
xfce4-menu-plugin &
xfsettingsd &
xfconfd &
xfwm4 &

配置完了启动文件,退出vi编辑器需要先按下键盘左上角的ESC键进入vi编辑器的命令模式,再敲击键盘输入:wq按下Enter键保存退出.
以上已经将服务器端的VNC配置大致做完了,现在进入阿里云控制台配置防火墙,在控制台的左侧安全栏点击防火墙(如下图).

右上角有一个添加规则的按钮,点击后出现弹框(如下图),协议选择TCP,端口号输入5901,最后点击确定按钮.

防火墙配置完成后,回到控制台重启服务器(这里一定要重启一遍),打开远程连接(如下图).

等到命令行窗口出现,输入vncserver :1启动VNC的服务.至此服务器的配置工作全部完成了.
远程连接
服务器已经配置完了,现在在本地windows电脑安装VNC客户端软件连接远程的桌面.
打开官网https://www.realvnc.com/en/connect/download/viewer/点击下载windows客户端软件安装.
软件安装完毕后点击运行,点击菜单栏File下的New connection出现弹出框.在VNC Server那一栏填入购买的阿里云主机的公网ip,后面添加上端口号:5901(如下图).配置完成点击OK.
此时主界面出现一个图标,点击后要求输入密码,这个密码正是上面在服务器端配置VNC Server设置的密码.

密码输入后,远程桌面被打开(如下图),到此所有Linux系统配置工作完成.
有时候发现阿里云服务器重启后远程桌面连接不上了,这时候需要打开阿里云控制台的命令行工具,输入vncserver :1将VNC的服务启动起来再进行远程连接.

Electron项目开发
图形化界面已经启动了,操作习惯和Windows相近.
现在开始开发Electron项目.目前githup上已经存在很多优秀的Electron脚手架,当然时间充裕又有特殊要求可以自己搭建.
脚手架对那些不熟悉Electron的前端玩家十分便利,免去了繁琐的项目搭建环节.目前人气旺的脚手架有基于react的electron-react-boilerplate,项目地址如下.
https://github.com/electron-react-boilerplate/electron-react-boilerplate
如果是vue发烧友,也可以选择electron-vue脚手架,项目地址如下.
https://github.com/SimulatedGREG/electron-vue
本文以electron-react-boilerplate为例,在Linux图形桌面上点击鼠标右键,选择Open Terminal Here打开命令行工具.
由于我们之前已经安装了git,此时就可以直接使用git将项目clone下来.
运行命令git clone https://github.com/electron-react-boilerplate/electron-react-boilerplate下载项目到桌面.
如果发现githup打不开,可以换成这个命令下载git clone git://github.com/electron-react-boilerplate/electron-react-boilerplate --depth 1.
下载完成后点击项目文件夹进入项目目录,为了接下来的安装工作顺利执行,将当前项目的文件夹的权限开放出来,执行命令sudo chmod -R 777 /home/admin/Desktop/electron-react-boilerplate.这里的项目名称默认是electron-react-boilerplate,如果改了其他名字这里也要相应修改.
权限修改完后进入项目文件夹,点击鼠标右键在此目录下打开命令行,输入sudo npm install -g cnpm,再使用sudo cnpm install安装依赖(npm安装可能会失败).依赖安装完毕后,打开项目文件package.json,发现项目启动命令是npm run start.
于是输入npm run start启动项目,启动过程中发现命令行工具卡住了(如下图),这是因为安装react的浏览器调试工具需要访问Chrome应用商店,因为无法访问所以命令行停滞不前.

为了解决上面问题,可以打开项目目录下src/main.dev.ts,将await installExtensions()那一行代码注释掉(如下图).这行注释仅仅是取消安装react的调试工具,Electron调试窗口依旧会保留.

项目终于启动起来了,打开项目目录下src/App.tsx,将根组件的代码修改为Hello world,代码和效果图如下.
const Hello = () => {
return (
<div>
Hello world
</div>
);
};
export default function App() {
return (
<Router>
<Switch>
<Route path="/" component={Hello} />
</Switch>
</Router>
);
}

这里建议实际开发中不要在Linux下编写代码,因为图形化桌面不太稳定经常出现卡顿影响开发效率.最优方案是在windows下编写代码,开发完后提交git.而Linux服务器直接从git上拉取最终的代码运行观察效果,如果所有效果都符合预期再打包成安装包.
运行shell脚本
electron-react-boilerplate脚手架基于react框架,开发页面的过程和我们平时开发网页没有区别.
如果仅仅只是开发网页相关内容,页面开发完就可以直接打包交付了.但我们的开发目标是要求能调用shell脚本来获取数据.数据的来源不再像之前那样从后端获取,而是直接调用脚本拿到要渲染的数据.
shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用shell来启动、挂起、停止甚至编写一些程序.
比如下面编写了一个简单的shell脚本文件getSystem.sh,获取系统的状态信息.
#!/bin/bash
echo -e "hostname:\t `hostname`"
echo -e "OScore:\t\t `uname -a`"
echo -e "CPUInfo:\t `grep "model name" /proc/cpuinfo | awk -F: '{print $2}' ` "
echo -e "CPUMHz:\t\t `grep "MHz" /proc/cpuinfo | awk -F: '{print $2}' ` "
echo -e "MemTotal:\t `free -m | awk '$1=="Mem:" {print $2}' ` "
echo代表输出,5条输出语句分别对应下面5条数据.
- 获取本机主机名
- 获取本系统的版本号
- 获取CPU的类型
- 获取CPU的工作频率
- 获取内存的使用情况
如何执行shell脚本文件呢?一般使用系统命令/bin/bash getSystem.sh就可以执行脚本文件,执行完后返回输出结果(如下图).

当前只是在命令行中输出了数据,接下来要在页面上将数据展现出来.
现在在项目根目录下src文件夹内新建一个文件夹scripts存放所有shell脚本,在里面新建一个文件getSystem.sh作为测试demo,并将上面脚本代码赋值到getSystem.sh里面.
shell脚本已经准备好了,我们还需要配置一下项目根目录下pacakge.json里build字段的下的files,将装有shell脚本的文件夹scripts配置进去(如下图).
这些files下配置的都是静态资源,相当于web开发中的public文件夹,webpack最终打包后会将它们原封不动的复制一份到打包后的目录下.

shell脚本的配置工作完成了,接下来如何在页面找到脚本的路径并执行呢?
由于Electron集成了nodejs,我们可以直接使用node的子进程模块child_process用来执行命令(代码如下).
const path = require("path");
const exec = require("child_process").exec;
const Hello = () => {
const getSystem = ()=>{ // 获取系统参数
//通过上面的配置,scripts文件夹将作为静态资源直接拷贝到打包后的文件夹里,通过下面方式便能访问到里面的文件
const script_path = path.join(__dirname, "scripts", 'getSystem.sh');
console.log(script_path); //shell 脚本的路径
exec(`/bin/bash ${script_path}`, (error,stdout) => { //执行脚本
if(!error){
console.log("脚本输出结果:"+stdout);
}
}
}
return (
<div>
<button onClick={getSystem}>点击</button>
</div>
);
};
按照预期,点击按钮最终就能返回脚本执行后的输出结果.但打包后测试,上面代码运行后控制台输出的脚本的路径是这样的(如下图).

我们会发现这个getSyetem.sh脚本文件在app.asar里面,app.asar是Electron程序的压缩格式文件,它相当于程序的外壳,将所有源代码全部否封装在自己的内部,形成了一个隔离的沙箱环境.
因此系统外部是无法获取到app.asar里面的文件,如果使用/bin/bash强行去执行包裹在app.asar里面的shell脚本是会报错的.
既然直接执行走不通,我们需要换一个思路,执行脚本文件时,将脚本拷贝到系统的某一个临时文件夹里,再使用/bin/bash去执行临时文件夹中的脚本就能顺利实现了.
我们新建一个工具方法call(代码如下).
/tmp就是Linux系统的临时文件夹,我们可以先判断临时文件夹下面有没有一个名叫myScripts的文件夹,没有就新创建一个.
myScripts文件夹创建完后,就将electron里面存放的静态脚本拷贝到myScripts文件夹下面,再调用/bin/bash执行脚本返回结果.
const path = require("path");
const exec = require("child_process").exec;
const fs = require('fs-extra');
//name是shell脚本的文件名词
export const call = (name)=>{
const script_path = path.join(__dirname, "scripts", `${name}.sh`);// 脚本在electron应用中的真实路径
return new Promise(async (resolve,reject)=>{
await fs.ensureDir('/tmp/myScripts'); //判断/tmp下面有没有myScripts文件夹,没有就创建一个
const dist_path = `/tmp/myScripts/${name}.sh`;
const isExit = await fs.pathExists(dist_path); // 脚本已经存在了吗
!isExit && await fs.copy(script_path, dist_path); // 如果不存在,就复制一份过去
//执行脚本
exec(`/bin/bash ${dist_path}`, (error, stdout) => {
if(error){
console.log(error);
reject(error);
}else{
resolve(stdout); //返回脚本的数据结果
}
});
})
}
}
页面上直接调用call函数,想调用那个脚本就把脚本的文件名传入即可.
const Hello = () => {
const getSystem = async ()=>{
const data = await call("getSystem"); // 调用 getSystem.sh脚本
console.log(data); //脚本执行完后返回的数据
}
return (
<div>
<button onClick={getSystem}>点击</button>
</div>
);
};
最终点击页面的按钮,就能让Linux系统执行getSystem.sh脚本,并返回执行结果.
数据获取的方式已经畅通了,接下来的工作就是开发页面渲染数据了,这部分的工作就和平时开发前端网页没有区别了.
打包
代码开发完毕后就进入打包环节.首先打开项目根目录下的package.json,在scripts下寻找打包命令,发现命令为package.
在命令行中直接输入sudo npm run package命令,打包进程就被启动了.脚手架比较智能,它会判断当前代码运行的平台,从而生成对应的安装包.
命令执行成功后,项目根目录release文件下会生成一个带.AppImage后缀的文件,这个文件就是Linux平台下最终生成的应用程序.
.AppImage和.rpm和.deb等安装格式不一样,它使用了通用的软件格式,几乎可以在所有的Linux发行版本中使用,一个包就能包含所有功能.
它就相当于Windows系统中的那些免安装的exe文件,可以通过软件传输、U盘拷贝来进行程序互传.其他的Linux机器只要接收到该程序,只需要使用鼠标双击就能启动应用程序,使用起来简单方便.
最终实现的效果如下.

源代码
转载:https://blog.csdn.net/brokenkay/article/details/117407950