前言
有需要编写前后端配合的软件,轻量且跨平台,快速就能开发一个属于自己的小工具。
思路
- Go语言负责后端,获取磁盘信息,连接数据库等前端无法胜任的活路
- Vue负责页面编写,调用后端接口,vue3一键生成开发模版,即刻上手,使用ElementPlus,美滋滋
- 使用Go内置技术,将前端dist包内嵌入程序中,单文件exe运行够清爽
- 每个客户端独立保存配置,可以用前端浏览器缓存,也可以后端用sqlite,都是轻量级
使用技术
- 前端:Vue3、Vite、ElementPlus、axios
- 后端:go1.20、systray、go-redis
开始操作
前端
封装axios请求后端api
js
import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: '/monitor-api',
// 超时
timeout: 40000
})
service.interceptors.response.use(res => {
// 拦截器
})
export default service
路由添加
js
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/monitor/index.vue')
},
{
path: '/redis',
name: 'redis',
component: () => import('@/views/redis/index.vue')
},
]
})
export default router
页面编写, 根据业务自己写, 打包应用yarn build
后端
初始化项目
shell
go mod init dmonitor
下载依赖
shell
go get -u github.com/redis/go-redis/v9
...
编写代码
- 主函数
- 通过
go:embed
指定静态资源目录, 可以直接将前端打包的资源封装入exe中 http.HandleFunc
将api前缀交给函数处理, 在函数里面具体处理子路由- 匿名函数
go func() {}()
, 不会阻塞主进程
go
//go:embed webapp
var f embed.FS
func main() {
// 端口配置可以通过运行参数指定
port := flag.Int("p", 40001, "server port")
base.RunPort = *port
flag.Parse()
addr := fmt.Sprintf(":%d", *port)
http.HandleFunc("/monitor-api/info", getInfoHandler)
http.HandleFunc("/monitor-api/redis/", redis.HandleApi)
st, _ := fs.Sub(f, "webapp")
// 将文件服务器注册到指定路径
http.Handle("/", http.StripPrefix("/", http.FileServer(http.FS(st))))
fmt.Printf("***********************app run on http://localhost:%d/ *******************", *port)
fmt.Println("")
utils.OpenBrowser(fmt.Sprintf("http://localhost:%d/", *port))
go func() {
utils.GenTaskBarIcon()
}()
fmt.Println("start http.... ", *port)
err := http.ListenAndServe(addr, nil)
if err != nil {
fmt.Println("start http error: ", err)
}
fmt.Println("start http success ", *port)
}
HandleApi
处理子路由
go
func HandleApi(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域
w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的类型
w.Header().Set("Content-Type", "application/json")
path := r.URL.Path
fmt.Println("redis HandleApi path:", path)
switch {
case strings.Contains(path, "/getRedisDbInfo"):
getRedisDbInfo(w, r)
case strings.Contains(path, "/changeRedisDb"):
changeRedisDb(w, r)
case strings.Contains(path, "/pingRedis"):
pingRedis(w, r)
case strings.Contains(path, "/initRedis"):
initRedis(w, r)
case strings.Contains(path, "/listKey"):
listKey(w, r)
case strings.Contains(path, "/getByKey"):
getByKey(w, r)
default:
http.NotFound(w, r)
}
}
- windows任务栏添加应用小图标和菜单
go
func GenTaskBarIcon() {
if runtime.GOOS == "windows" {
systray.Run(onReady, onExit)
}
}
func onReady() {
systray.SetIcon(iconData)
systray.SetTitle("D-Monitor")
systray.SetTooltip("D-Monitor 右键点击打开菜单!")
menuOpen := systray.AddMenuItem("打开网页", "打开系统网页")
systray.AddSeparator()
menuQuit := systray.AddMenuItem("退出", "退出程序")
go func() {
for {
select {
case <-menuOpen.ClickedCh:
OpenBrowser(fmt.Sprintf("http://localhost:%d/", base.RunPort))
case <-menuQuit.ClickedCh:
systray.Quit()
os.Exit(0)
}
}
}()
}
func onExit() {}
- 针对linux和windows写不同的工具函数, 因为不同平台用的函数可能不一样, 会造成编译失败, go只能针对文件级别来区别平台编译, 所以在文件开头加入注释
//go:build [platform]
go
// utils/linux.go
//go:build linux
package utils
func OpenBrowser(url string) error {
return nil
}
go
// utils/windows.go
//go:build windows
package utils
func OpenBrowser(url string) error {
var cmd string
var args []string
switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
default: // Linux 等其他 Unix-like 系统
cmd = ""
}
if cmd == "" {
return nil
}
args = append(args, url)
ec := exec.Command(cmd, args...)
if runtime.GOOS == "windows" {
ec.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}
return ec.Start()
}
- 打包脚本
build.sh
shell
#!/bin/bash
set -eu
VERSION=$(git describe --abbrev=0 --tags)
REVCNT=$(git rev-list --count HEAD)
DEVCNT=$(git rev-list --count $VERSION)
if test $REVCNT != $DEVCNT
then
VERSION="$VERSION.dev$(expr $REVCNT - $DEVCNT)"
fi
echo "VER: $VERSION"
GITCOMMIT=$(git rev-parse HEAD)
BUILDTIME=$(date -u +%Y/%m/%d-%H:%M:%S)
LDFLAGS="-X main.VERSION=$VERSION -X main.BUILDTIME=$BUILDTIME -X main.GITCOMMIT=$GITCOMMIT"
if [[ -n "${EX_LDFLAGS:-""}" ]]
then
LDFLAGS="$LDFLAGS $EX_LDFLAGS"
fi
build() {
echo "$1 $2 $3... $LDFLAGS"
LDFLAGS1="$LDFLAGS"
if [ "$1" = "windows" ]; then
LDFLAGS1="$LDFLAGS -H=windowsgui"
fi
echo "LDFLAGS1... $LDFLAGS1"
GOOS=$1 GOARCH=$2 go build \
-ldflags "-s -w $LDFLAGS1" \
-o dist/dmonitor-${3:-""}
}
build linux arm linux-arm
# build darwin amd64 mac-amd64
build linux amd64 linux-amd64
build linux 386 linux-386
build windows arm64 win-arm64.exe
build windows amd64 win-amd64.exe
后言
至此就能开发一个自己的轻量小工具,目前通过go+vue开发了一个服务端监控软件, 可以实时监控服务器的CPU、内存、磁盘、进程等使用情况,也集成了简易的redis客户端,可以连接到redis并查看键值, upx
压缩后也就2M左右,够轻便了
源码地址
- 源码和程序截图详见github.com/dhjz/dmonit...