Flutter、Electron、Tauri框架对比及Electron+Vue项目实战

随着移动互联网和桌面应用的快速发展,开发者们越来越倾向于使用跨平台框架来提高开发效率和降低成本。Flutter、Electron和Tauri作为当前最受欢迎的几种跨平台框架,各自拥有独特的优势和应用场景。本文旨在深入探讨这三种框架的特点、优缺点,并分享一个基于Electron+Vue的项目实战经验。

背景

XXXX团队:我们想在断网环境下通过 RuntimeAI 运行 Python 程序,给用户输出日志、执行结果等需求的桌面端程序。
我:好的,上菜。

三大框架对比

Flutter简介

Flutter是Google推出的开源UI软件开发工具包,用于构建在iOS、Android、Web和桌面端高质量的原生界面。Flutter的一大特点是其“一次编写,处处运行”的能力,极大地提高了开发效率。Flutter使用Dart语言,拥有丰富的组件库和良好的性能。

Electron简介

Electron是一个使用JavaScript、HTML和CSS构建桌面应用的框架,由GitHub开发。它允许开发者将Web技术用于桌面软件的开发,使得原本只能在网络浏览器中运行的应用能够被作为独立的桌面应用来分发和安装。Electron应用本质上是Chromium浏览器的变种,这意味着它相对较重,但开发速度快,易于上手。

Tauri简介

Tauri 是一个相对较新的框架,旨在提供一个安全、轻量级的平台,用于将Web技术包装成桌面应用。与Electron相比,Tauri的最大优势在于其对安全性的强调以及更小的应用体积。Tauri应用使用Rust语言进行系统调用,提供了更好的性能和更低的资源消耗。

特点FlutterElectronTauri
开发语言DartJavaScript, HTML, CSSJavaScript, HTML, CSS
跨平台移动、Web和桌面应用开发桌面应用开发桌面应用开发
性能高(Flutter直接编译到机器代码,减少了桥接层)中(基于Chromium和Node.js,可能占用更多资源)高(生成的应用体积小,性能优化)
用户界面丰富的UI组件库,可高度自定义使用Web技术构建UI,灵活使用Web技术构建UI,灵活
应用体积较大(包含Chromium和Node.js)
安全性依赖于框架本身和开发实践依赖于框架本身和开发实践,可能需要注意Web安全问题高(Rust的安全性优势)
适用场景适合需要高性能和高度定制UI的跨平台应用适合希望快速将Web应用转为桌面应用的项目适合对应用体积和性能有严格要求,注重安全的桌面应用开发
社区支持
GitHub Star161k111k75.5k
数据对比

框架Demo

根据三大框架官方文档,安装了开发和构建依赖、配置环境变量和成功启动后,我将通过启动速度、目录结构、打包体积做真实对比。

统一开发IDE为 VS Code

  1. 启动速度:Electron > Tauri > Flutter

启动速度只做大概预估,因为跟机器性能、网速和Tauri是否有新的依赖需要更新有关。

  1. 目录结构:Electron 、Tauri > Flutter

目录结构对比

  1. 打包体积:Flutter > Tauri > Electron

请添加图片描述

框架选择

基于以上对比数据,我选择Electron,理由是:

  1. 开发难度小
  2. 安装依赖方便
  3. 更加适合本次需求
  4. VS Code也是利用Electron开发
  5. 社区庞大

Electron+Vue3项目实战基础版)

如果安装 electron、electron-builder和打包出错。中心思想就是自己下载好所需依赖,放至对应目录下。

搭建Electron应用参考文章,部分需要做修改。

  1. 项目初始化

首先,按照 Vue 官网步骤创建一个Vite+Vue+Ts项目:

npm create vue@latest
cd my-project
code .
npm install
  1. 添加Electron依赖:
npm install electron vite-plugin-electron -D
  1. 修改package.json

dist-electron:vite-plugin-electron插件运行后,会将electron主进程文件编译到dist-electron/index.js

chcp 65001:解决中文乱码

去掉 “type”: “module”

"main": "dist-electron/index.js"
"scripts": {
  "dev": "chcp 65001 && vite"
  ...
}
  1. 配置vite.config.ts
import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import electron from 'vite-plugin-electron'

export default defineConfig({
  plugins: [
    vue(),
    electron({
      entry: "electron/index.ts"
    })
  ]
})

  1. 创建 electron/index.ts 主进程文件
import { app, BrowserWindow } from "electron";

let win;

const createWindow = () => {
  win = new BrowserWindow({
    webPreferences: {
      contextIsolation: false, // 允许渲染器脚本访问预加载脚本中的全局变量和模块
      nodeIntegration: true // 允许在渲染进程中使用node
    },
    title: "Main window",
    width: 800,
    height: 600
  });
  // 判断当前环境,加载不同html文件
  if (process.env.NODE_ENV != "development") {
    win.loadFile("dist/index.html");
  } else {
    win.loadURL(`${process.env["VITE_DEV_SERVER_URL"]}`);
  }
};
// electron资源加载完毕,创建窗口
app.whenReady().then(createWindow);
// 窗口关闭,退出electron
app.on("window-all-closed", () => {
  app.quit();
});

  1. 运行Electron应用
npm run dev

请添加图片描述

  1. 添加打包应用的依赖
npm i electron-builder -D
  1. package.json添加打包脚本和build配置

可以自定义应用的logo,详情参考官方文档

"scripts": {
	"build": "vite build && electron-builder"
},
"build": {
	"appId": "electron.desktop",
    "productName": "vue-electron",
    "asar": true,
    "copyright": "Copyright © 2024",
    "directories": {
      // exe文件输出到release下
      "output": "release/"
    },
    "files": [
      "dist/**",
      "dist-electron/**"
    ],
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,
      "perMachine": false,
      "allowToChangeInstallationDirectory": true,
      "deleteAppDataOnUninstall": false
    },
}
  1. 打包并运行
npm run build

运行命令后,会生成release文件夹,其中包含了可执行文件vue-electron_0.0.0.exe。安装即可

请添加图片描述

Electron+Vue3项目实战(进阶版)

给基础版添加新功能:前端点击按钮 --> 调用机器的Python程序 --> 执行自定义py脚本 --> 返回结果给前端

前提:机器已经成功安装 Python3

  1. 新增py脚本
# 根目录下新增scripts/script.py
print("hello world")
  1. 利用node调用Python并执行py脚本
import { app, BrowserWindow } from 'electron'
import { spawn } from 'child_process'
import path from 'path'

let win;
const createWindow = () => {
  win = new BrowserWindow({
    webPreferences: {
      contextIsolation: false,
      nodeIntegration: true,
    },
    title: "Main window",
    width: 800,
    height: 600
  })

  if(process.env.NODE_ENV != "development") {
    win.loadFile("dist/index.html")
  } else {
    win.loadURL(`${process.env["VITE_DEV_SERVER_URL"]}`)
  }
}
// start 创建node子进程,执行脚本
const pythonProcess = spawn('python', [path.join(process.cwd(), 'scripts/script.py')])
pythonProcess.stdout.on('data', res => {
    //                  res为buffer格式,需要转换字符格式
  console.log('success',res.toString());
})
pythonProcess.stderr.on('data', err => {
  console.log('err', err.toString());
})
// end

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  app.quit()
})
  1. 终端查看结果
build started...
✓ 1 modules transformed.
dist-electron/index.js  0.95 kB │ gzip: 0.55 kB
built in 33ms.

success hello world
  1. 结合渲染进程获取执行结果(进程通信)

主进程与渲染进程

npm i vite-plugin-electron-renderer -D
// vite.config.ts
import electronRender from 'vite-plugin-electron-renderer'

plugins: [
    vue(),
    electron({
      entry: "electron/index.ts"
    }),
    electronRender()
],
  1. 前端发送消息给主进程
<script setup lang="ts">
import { ipcRenderer } from 'electron'
// 添加点击事件,发送消息给主进程
const runPy = () => {
  ipcRenderer.send('run')
}
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" @click="runPy"/>
  </header>
</template>

  1. 主进程开始执行
// 首先将执行的子进程提取出来
const run = () => {
  return new Promise((resolve, reject) => {
    const pythonProcess = spawn('python', [path.join(process.cwd(), 'scripts/script.py')])
    pythonProcess.stdout.on('data', res => {
      resolve(res.toString())
    })
    pythonProcess.stderr.on('data', err => {
      reject(err.toString())
    })
  })
}

// 主进程监听到渲染进程的消息后,立马执行run函数,执行结束后再通知渲染进程
ipcMain.on('run', () => {
  run()
    .then(res => {
      win.webContents.send("finish", res)
    })
    .catch(err => {
      win.webContents.send("finish", err)
    })
})
  1. 前端接收结果
<script setup lang="ts">
import { ipcRenderer } from 'electron'
import { ref } from 'vue'

const runPy = () => {
  ipcRenderer.send('run')
}
const result = ref()
// 监听主进程发送的finish消息。实际发送的数据在回调的第二个参数中
ipcRenderer.on('finish', (_, data:any) => {
  result.value = data
})
</script>

<template>
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" @click="runPy"/>
    <p>子进程执行Python后的结果:<b>{{ result }}</b></p>
  </header>
</template>

请添加图片描述

结语

  1. Electron、nodejs有大量api需要在实践中慢慢摸索,以上只是冰山一角
  2. 奇奇怪怪的报错,大概率是node版本过低导致
  3. 打包后的Flutter可执行文件无法显示,但有对应的进程在跑。修改如下代码:
// windows\runner\flutter_window.cpp

// 只留下 this->Show();
//flutter_controller_->engine()->SetNextFrameCallback([&]() {
  this->Show();
//});
GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐