桌面跨平台框架选型
从兴趣使然到从业,本人用过的跨平台GUI框架也不少,特地写这篇文章总结下经验,希望能帮到你。接下来就开始我恬不知耻的侃侃而谈,不一定对,仅我本人拙见。
见标题👆🏼👆🏼👆🏼,因为是桌面跨平台开发的文章,所以文中不会提到原生或移动端开发的技术。
百花齐放的框架 跨平台开发这个词已经不新鲜了,也有许多的成熟的框架被大家使用和比较着。现在是2025年中旬 。桌面端讨论最多的是:
另外我还想在这提及下其他的较热门的框架
React Native(也对桌面端进行了支持)
Wails(Golang + Webview的 GUI方案)
Fyne(Golang GUI方案)
Avalonia(.net方案)
Web渲染和Native的优劣
Web对比Native的优势
对比原生渲染,web开发的优势就是社区生态丰富、样式控制方便、开发效率高、开发成本低。原子化CSS流行到2025年了,Tailwind 、Unocss 、Windcss 等库的周边生态越来越健壮,各种妙曼动画库玲琅满目,能够很轻松的做出酷炫的效果。
Web对比Native的劣势
老生常谈的就是性能问题 ,尤其是在3D渲染场景显得更加捉襟见肘。其次,就是对native原生功能的使用比较局限(其实Electron这方面相当出色,其他桌面跨平台方案也没有很差,不过几乎所有的移动端跨平台方案对原生api的使用都比较局限)。
除此之外,有一个比较突出的缺点就是「出活太快,显得自己没水平。工作不够饱和。」
我想着重跟大家探讨下Electron、Wails这三个
他们的共同的优缺点就不过多将了,实际上就是web套壳的优缺点。
Electron 说到Electron,绕不开的话题就是Chromium 。它给Electron带来了极高的兼容性适配,也是Electron在多平台上的兼容性表现出色的核心原因。不过带来的问题就是构建出来的产物太大,不过多赘述了。看图👇🏻👇🏻👇🏻
较小的心智负担 Electron在所有的Web跨平台框架中,算是历史悠久的了。遇到问题在网上总能查到解决方案,所有的坑都有老前辈替我们踩过了。对系统级API的开放也比较丰富。在整个开源和闭源项目中也有很多Awesome Project ,比如:
网上的解决方案很多,可以说是心智负担最小的Web框架了。这段内容中,我主要想表明他是一个生态繁荣 的框架。
兼容性满屌的 对比其他web框架,Electron的兼容性可以说相当的屌了。开发者无需过多担心兼容性问题,在使用其他webview类的框架开发时,可能会经常遇到以下情况:
“这个图标在mac上好好的,怎么在windows上歪到姥姥家了”
“这个CSS规则怎么在mac上不生效”
“Linux arm64上没跑过,跑一下不会炸肛吧”
“🐴的,用Electron写早下班了”
等
学习成本低 渲染进程就是纯粹写Web,主进程使用Node。对于前端开发人员比较友好,不用花时间去学习其他的语言,只要会node。写客户端就跟写老本行一样欢愉。
构建方便 构建产物这方面,Electron绝对的牛,各种开发脚手架和构建工具玲琅满目,我用的最多的脚手架就是Electron-Vite 。另外Electron-Egg都挺不错的。构建配置上,Electron-Forge和Builder也提供了相当丰富的配置项,能满足绝大部分场景的需求。在开发环境使用Vite构建也能享受到极致的HMR体验。
背后有大哥 背靠Github和微软,Bug少、更新快、论坛好。
Wails 这是一个类似Tauri的一个Webview解决方案,不过是使用Go来做访问底层和系统交互。不过这个框架还比较年轻,总体来说就是系统级接口不够,兼容性一般,打包体验一般。不过,这些缺点也是Webview方案的缺点。我个人挺喜欢这个项目,前景也是相当不错的。如果是个Gopher应该,应该难以拒绝这个框架。
有兴趣的厚密可以看下我这个项目,就是使用Wails写的。
Transok - 基于 wails + react 实现的局域网文件传输工具
产物极极极小 因为使用的webview + Go,所以构建出的产物极小(Go本身就是一个产物很轻量的语言),对比Electron的产物可以说是草履虫级别了。不过包体积小并不代表运行效率,内存使用率就低。事实上web跨平台方案的运行效率都半斤八两。
前后端通讯优雅 通常,我们写Electron最大的场景就是进程通讯,进程通讯说破天也就下面几种方案。
关闭上下文隔离直接使用ipc通信
开启上下文隔离,在预渲染进程中写contextBridge api
使用webContents通信
使用Http传统B/S请求
使用remote模块直接硬搞(已废弃)
Wails提供了更优雅的方式
只需要在Wails中绑定了Go结构体实例,写好接口方法,就能自动生成Ts/Js的函数签名,渲染层直接调用就行。实现了自动的Go To Js 的转换。
1 2 3 4 5 6 7 8 9 10 11 ... Bind: []interface {}{ sysSvc, fileSvc, storageSvc, ginSvc, shareSvc, discoverSvc, mdns_handlers.GetDiscoverHandler(), } ...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 package servicesimport ( "context" "encoding/json" "os" "path/filepath" "strings" "sync" "github.com/wailsapp/wails/v2/pkg/runtime" ) type fileService struct { ctx context.Context } type FileInfo struct { Path string Name string Size int64 Type string Text string } var file *fileServicevar fileOnce sync.Oncefunc File () *fileService { if file == nil { fileOnce.Do(func () { file = &fileService{} }) } return file } func (c *fileService) Start(ctx context.Context) { c.ctx = ctx } func (c *fileService) SelectFiles() []string { files, err := runtime.OpenMultipleFilesDialog(c.ctx, runtime.OpenDialogOptions{ Title: "选择文件" , }) if err != nil { return nil } return files } func (c *fileService) GetFile(path string ) *FileInfo { fileInfo, err := os.Stat(path) if err != nil { return nil } return &FileInfo{ Path: path, Name: fileInfo.Name(), Size: fileInfo.Size(), Type: strings.TrimPrefix(strings.ToLower(filepath.Ext(path)), "." ), } } func (c *fileService) GetShareList() []FileInfo { storage := Storage() shareList, isExist := storage.Get("share-list" ) result := make ([]FileInfo, 0 ) if !isExist { storage.Set("share-list" , result) return result } if jsonData, err := json.Marshal(shareList); err == nil { json.Unmarshal(jsonData, &result) } validFiles := make ([]FileInfo, 0 ) for _, file := range result { if file.Type == "pure-text" { validFiles = append (validFiles, file) } if _, err := os.Stat(file.Path); err == nil { validFiles = append (validFiles, file) } } storage.Set("share-list" , validFiles) return validFiles }
就上上述代码一样,在wails构建时,就会生成对应的申明文件在渲染层
1 2 3 4 5 6 7 8 9 10 11 12 import {services} from '../models' ;import {context} from '../models' ;export function GetFile (arg1:string ):Promise <services.FileInfo >;export function GetShareList ( ):Promise <Array <services.FileInfo >>;export function SelectFiles ( ):Promise <Array <string >>;export function Start (arg1:context.Context ):Promise <void >;
调用起来非常之方便
开发体验好 Wails没有什么特定语法和对语言层面的魔改,开发者仅需纯粹的掌握Go和Web前端即可,侵入性低。可以自由的使用Go或者Js的生态。例如可以在Go侧使用Gin、Gorm、Casbin;在Web侧使用Vite、React、Shadcn…沉浸式开发无需过多担心语言直接的割裂感。
目前不足
生态不够健全(毕竟新框架,需要沉淀)
系统级API不够
v2版本对多窗口支持不够
构建体验不好,在mac和win上还行,linux不太好
总的来说
个人拙见
本人不太会Tauri(其实是不会Rust),没有能力从Tauri开发者角度来说明这个框架。不过weview方案都大同小异,相信未来webview会越来越好。就目前来说,无论从什么角度来讲,大项目或者公司业务还是优先考虑electron(如果你想按时下班的话,老老实实别折腾)。
如果对系统级API依赖性不高或者是小工具可以使用webview的方案。
如果您的业务涉及到各种Linux适配(有国产信创系统的适配),老老实实Electron
如果您对构建产物包要求比较敏感,可以尝试webview
以上👋 ;
前端 , 技术分享 — 2025年5月26日