知足常乐

知足常乐

快速开发一个客户端软件?

2021-12-19

最近发现顺手的工具可以极大的提高工作的效率,海量的密钥信息,网站信息,存储在chrome的标签中还是不方便。所以我就去找免费开源的工具,发现要么功能很复杂,要么好看的要收费。所以我选择自己写一个简单好看又好用的数据管理工具。
注意:开发过程中建议科学上网,因为一些依赖国内网络状况会导致依赖下载很慢而编译失败。

Hello World

学习任何语言的时候,我都习惯先写一个Hello World。

保证自己电脑上有node.js,我这台电脑上没有装过nodejs,直接去官网下载了一个最新版本的。

image.png

打开idea创建一个空的项目 在根目录打开终端

#安装 electron
npm install electron -g

#初始化项目
npm init -y

项目结构

image.png

新建index.js

const electron = require('electron');

const {
    app, // 控制应用生命周期的模块
    BrowserWindow, // 创建原生浏览器窗口的模块
} = electron;

// 保持一个对于 window 对象的全局引用,如果不这样做,
// 当 JavaScript 对象被垃圾回收, window 会被自动地关闭
let mainWindow;

function createWindow() {
    // 创建浏览器窗口。
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // 加载应用的 index.html。
    // 这里使用的是 file 协议,加载当前目录下的 index.html 文件。
    // 也可以使用 http 协议,如 mainWindow.loadURL('http://nodejh.com')。
    mainWindow.loadURL(`file://${__dirname}/index.html`);

    // 启用开发工具。
    mainWindow.webContents.openDevTools();

    // 当 window 被关闭,这个事件会被触发。
    mainWindow.on('closed', () => {
        // 取消引用 window 对象,如果你的应用支持多窗口的话,
        // 通常会把多个 window 对象存放在一个数组里面,
        // 与此同时,你应该删除相应的元素。
        mainWindow = null;
    });
}

// Electron 会在初始化后并准备
// 创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow);

// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
    // 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
    // 否则绝大部分应用及其菜单栏会保持激活。
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    // 在 macOS 上,当点击 dock 图标并且该应用没有打开的窗口时,
    // 绝大部分应用会重新创建一个窗口。
    if (mainWindow === null) {
        createWindow();
    }
});

新建index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js and Electron
</body>
</html>

Run起来

#启动程序
electron .

image.png

最简单的electron项目就跑了起来~

什么是electron?

image.png
如果你去搜索这个问题,大多数浏览器会告诉你 Electron 是一个跨平台的、基于 Web 前端技术的桌面 GUI 应用程序开发框架。重点是基于Web前端技术,有过客户端开发经验的同学本能都会觉得写客户端软件应该需要C/C++/C# 之类的语言来开发。但是 Electron 把Web端技术带到了客户端!而且上手容易,开发成本低,因为它使用的是nodejs语言!

快速开发

对于非专门研究技术的选手(我)使用脚手架开发效率是最高的。我对比几款脚手架之后选择了 electron-vue ,因为我写过一段时间的vue,所以上手应该更快点。

初始化项目

打开你的终端,安装vue-cli之后初始化一个项目结构。
在第六步的时候建议全部安装
image.png

# 安装 vue-cli 和 脚手架样板代码
npm install -g vue-cli
vue init simulatedgreg/electron-vue exhale

# 安装依赖并运行你的程序
cd exhale
yarn # 或者 npm install
yarn run dev # 或者 npm run dev

初始化完成我们就可以使用Idea打开这个项目,安装依赖、跑起来。

image.png

自定义配置

不难发现,electron默认界面还是挺丑的,所以我们先对一些配置进行自定义化

无边框

首先我们要把默认的边框去掉。找到src/main/index.js文件
设置frame属性为false

  /**
   * Initial window options
   */
  mainWindow = new BrowserWindow({
    height: 600,
    width: 800,
    useContentSize: true,
    frame:false,
  })

窗口设置无边框之后,默认界面是无法拖拽的。我们可以通过CSS属性来控制拖拽范围

应用程序需要在 CSS 中指定 -webkit-app-region: drag 来告诉 Electron 哪些区域是可拖拽的
在可拖拽区域内部使用 -webkit-app-region: no-drag 则可以将其中部分区域排除

我们在App.vue页面的#app div加上可以拖拽的属性。这样我们的无边框应用就设置好了。
image.png

最小化、最大化、关闭

无边框把原生的最小化、最大化、关闭按钮也去掉了。
所以我们要自己实现这些功能。
在这里我选择熟悉的Element-ui组件库来画几个图标

#安装element-ui
npm i element-ui -S

在src\renderer\main.js导入ElementUI

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

简单的在App.vue页面右上角画了三个图标
image.png

然后在App.vue页面,引入ipcRenderer,来实现了三个按钮的点击事件,
这里的ipcRenderer把它想成一个通信兵,你让它发送的命令,它都会发送到主进程(真正操作软件的地方)里边。

<script>
let ipcRenderer = require('electron').ipcRenderer;
export default {
    name: 'exhale',
    methods:{
      minus:function (){
        console.info("最小化")
        //发送最小化命令
        ipcRenderer.send('window-min');
      },
      plus:function (){
        console.info("最大化")
        //发送最大化命令
        ipcRenderer.send('window-max');
      },
      close:function (){
        console.info("关闭")
        //发送关闭命令
        ipcRenderer.send('window-close');
      }
    }
  }
</script>

回到主进程文件src/main/index.js中
在头部引入ipcMain组件
然后监听我们在渲染进程中发送的三个事件,
实现对应的方法

import { ipcMain,app, BrowserWindow } from 'electron'
...
...
//接收最小化命令
ipcMain.on('window-min', function() {
  mainWindow.minimize();
})
//接收最大化命令
ipcMain.on('window-max', function() {
  if (mainWindow.isMaximized()) {
    mainWindow.restore();
  } else {
    mainWindow.maximize();
  }
})
//接收关闭命令
ipcMain.on('window-close', function() {
  mainWindow.close();
})

ok,这样我们就实现了自定义最小化、最大化、关闭三个按钮。

01.gif

一般客户端软件都是会在后台运行,也就是右下角会有一个托盘图标来显示当前应用在后台运行中
而且这个托盘图标还支持很多功能等等,我们来实现它。

在主进程文件src/main/index.js中
头部文件中引入 Menu, Tray 组件
然后在 createWindow 初始化方法中设置图标的相关属性

import { ipcMain,app, Menu, Tray,BrowserWindow } from 'electron'

//...
//...
//...

function createWindow () {

	//...
	//...
	//...  

  //设置icon的位置
  tray = new Tray("static/logo.png")
  const contextMenu = Menu.buildFromTemplate([
    {
      label: '退出', click: () => {
        app.quit();
      }
    },
  ])

  //双击托盘图标显示主页面
  tray.on('double-click',()=>{
    mainWindow.show()
  })

  //设置托盘图标提示文本信息
  tray.setToolTip('app')
  tray.setContextMenu(contextMenu)

}

现在这个时候,我们发现,我们的关闭按钮写错了,其实是隐藏主进程到后台,让托盘提供关闭功能
我们把close命令改成hide命令


//接收关闭命令
ipcMain.on('window-close', function() {
  mainWindow.hide();
})

按照上边设置好之后。

托盘
image.png

提示
image.png

退出
image.png

打包

看起来我们的app只剩下把页面写好就行了,这个步骤就需要细心的开发了。
我们开发完之后,如何打包出来一个安装程序供别人使用呢?

打包的话,我们得需要一个logo,
打开package.json文件修改我们的icon位置

    "mac": {
      "icon": "static/logo.png"
    },
    "win": {
      "icon": "static/logo.png"
    },
    "linux": {
      "icon": "static/logo.png"
    }

其实我们打开package.json文件时可以发现,脚手架已经帮我们做好了。

  "scripts": {
    "build": "node .electron-vue/build.js && electron-builder",
    "build:dir": "node .electron-vue/build.js && electron-builder --dir",
    "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
    "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
    "dev": "node .electron-vue/dev-runner.js",
    "pack": "npm run pack:main && npm run pack:renderer",
    "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
    "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
    "postinstall": ""
  },

看样子我们只需要运行 yarn run build 命令就行了
可是,我运行之后是报错了。

E:\WebStorm\exhale\.electron-vue\build.js:45
  const tasks = new Listr(
        ^

SyntaxError: Identifier 'tasks' has already been declared

搜索一番后,找到.electron-vue\build.js文件,确实是重名了,我们修改一下名字

image.png

重新打包仍然报错

E:\WebStorm\exhale\.electron-vue\build.js:38
  const m = new Multispinner(taskss, {
            ^

ReferenceError: Multispinner is not defined

编译过程中没有地方用到这段逻辑,果断注释掉。已经向官方提了BUG

image.png

再次打包
可以看到 build/win-unpacked 目录下会打出来一个 exhale Setup 1.0.0.exe的安装包
运行就可以安装了。

image.png

下载使用

上边就是开发一个客户端所需要的基础知识,如果有兴趣可以学习electron官方文档。
我按照自己的需求开发了一款工具,有兴趣的话可以下载使用~
image.png

https://github.com/FangPengbo/exhale

参考文档