如何利用 `Tauri` 或 `Electron`,将 Vue 应用打包为功能强大的桌面应用,并与原生系统 API 交互?

各位观众,各位朋友,晚上好!我是你们的老朋友,代码界的段子手。今天咱们不讲段子,讲点实在的,聊聊如何把Vue这匹小野马,驯服成一个桌面端的可靠座驾,也就是如何用Tauri或Electron把Vue应用打包成桌面应用,并且让它能和咱们电脑的原生系统API眉来眼去。

咱们这次的讲座,分成三个部分:

  1. 选妃记:Tauri vs. Electron,谁更适合你?
  2. Vue的华丽变身:从网页到桌面,这中间发生了什么?
  3. 勾搭原生API:让你的桌面应用更懂你。

一、 选妃记:Tauri vs. Electron,谁更适合你?

话说这后宫佳丽三千,各有千秋。Tauri和Electron,都是打包Vue应用的利器,但脾气秉性大不相同。咱们先来了解一下她们的背景:

  • Electron: 这位是老牌贵族,出身名门,基于Chromium和Node.js。简单粗暴,直接把整个浏览器内核塞到你的应用里,优点是兼容性好,社区庞大,啥问题都能找到人问。缺点是体积庞大,吃内存像个无底洞。

  • Tauri: 这是后起之秀,血统纯正,基于Rust和WebView (比如Windows上的WebView2, MacOS上的WKWebView)。身轻如燕,性能卓越,安全性高,启动速度快。缺点是生态不如Electron完善,有些坑需要自己填。

咱们用一张表格来对比一下:

特性 Electron Tauri
体积 巨大,动辄几百MB 苗条,通常几十MB
性能 消耗资源较多,启动速度相对较慢 消耗资源少,启动速度快
安全性 由于基于Node.js,存在一定的安全风险,需要开发者自己加强安全防护 使用Rust,安全性更高,避免了许多常见的Web安全漏洞
技术栈 JavaScript, HTML, CSS, Node.js Rust (后端), JavaScript, HTML, CSS (前端)
社区 庞大,资源丰富,遇到问题容易找到解决方案 相对较小,但发展迅速,贡献者积极
原生API 访问方便,可以直接调用Node.js的API 需要通过桥接机制(如Command)进行调用,相对复杂一些
更新 更新包通常较大,因为需要更新整个Chromium内核 更新包较小,只需更新应用代码

结论:

  • 如果你追求快速开发、兼容性好、社区支持完善,并且对应用体积和性能不太敏感,那么Electron是你的不二之选。
  • 如果你追求极致性能、更小的体积、更高的安全性,并且愿意学习Rust,那么Tauri会给你带来惊喜。

二、 Vue的华丽变身:从网页到桌面,这中间发生了什么?

不管是选择Tauri还是Electron,咱们首先得有一个Vue应用。这部分我就假设你已经手握一个写好的Vue项目了。

1. Electron:Vue + Electron 的快速姿势

  • 安装Electron:

    npm install electron --save-dev
  • 创建主进程文件 (main.js): 这是Electron应用的核心,负责创建窗口、加载网页、处理系统事件等等。

    const { app, BrowserWindow } = require('electron')
    const path = require('path')
    
    function createWindow () {
      const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          nodeIntegration: true, // 允许使用Node.js API
          contextIsolation: false // 关闭上下文隔离,方便在渲染进程中使用Node.js API
        }
      })
    
      win.loadFile('dist/index.html') // 加载Vue应用的入口文件
      // win.loadURL('http://localhost:8080') // 如果你的Vue应用是运行在开发服务器上,可以这样加载
    }
    
    app.whenReady().then(() => {
      createWindow()
    
      app.on('activate', () => {
        if (BrowserWindow.getAllWindows().length === 0) {
          createWindow()
        }
      })
    })
    
    app.on('window-all-closed', () => {
      if (process.platform !== 'darwin') {
        app.quit()
      }
    })

    解释一下:

    • nodeIntegration: true 允许在渲染进程(Vue应用)中使用Node.js API。注意: 在生产环境中,强烈建议关闭此选项,并使用contextBridge来安全地暴露Node.js API。
    • contextIsolation: false 关闭上下文隔离,简化开发,但降低安全性。在生产环境中,应该启用此选项,并使用contextBridge来安全地暴露API。
    • win.loadFile('dist/index.html') 加载打包好的Vue应用的入口文件。 你需要先运行 npm run build 或者 yarn build 来生成 dist 目录。
  • 修改 package.json: 添加启动脚本,方便运行Electron应用。

    {
      "scripts": {
        "start": "electron ."
      }
    }
  • 运行Electron应用:

    npm run build  # 构建Vue应用
    npm start    # 启动Electron应用

2. Tauri:Vue + Tauri 的优雅之道

  • 安装Tauri CLI:

    cargo install tauri-cli

    确保你已经安装了Rust和Cargo。

  • 创建Tauri项目:

    tauri init

    这个命令会提示你选择前端框架,选择Vue,然后一路回车。

  • 配置Tauri: tauri.conf.json 文件是Tauri的核心配置文件。你需要修改它来指定Vue应用的入口文件。

    {
      "build": {
        "beforeBuildCommand": "npm run build", // 构建Vue应用的命令
        "beforeDevCommand": "npm run dev",  // 启动Vue开发服务器的命令
        "devPath": "http://localhost:3000", // Vue开发服务器的地址
        "distDir": "../dist"                // Vue应用的打包目录
      },
      "package": {
        "productName": "MyTauriApp",
        "version": "0.1.0"
      },
      "tauri": {
        "bundle": {
          "identifier": "com.example.mytauriapp",
          "icon": [
            "icons/32x32.png",
            "icons/128x128.png",
            "icons/[email protected]",
            "icons/icon.icns",
            "icons/icon.ico"
          ]
        },
        "security": {
          "csp": null  // 允许所有内容,生产环境需要配置更严格的CSP
        },
        "updater": {
          "active": false
        },
        "windows": [
          {
            "width": 800,
            "height": 600,
            "resizable": true,
            "title": "My Tauri App"
          }
        ]
      }
    }

    解释一下:

    • beforeBuildCommandbeforeDevCommand 分别指定了构建和开发Vue应用的命令。
    • devPath 指定了Vue开发服务器的地址。
    • distDir 指定了Vue应用的打包目录。
  • 运行Tauri应用:

    npm run build   # 构建Vue应用 (可选,如果你已经构建过了)
    npm run tauri dev  # 启动Tauri开发模式
    npm run tauri build # 构建Tauri应用

三、 勾搭原生API:让你的桌面应用更懂你

现在,你的Vue应用已经成功地穿上了桌面应用的外衣。但是,这还不够。我们需要让它能和操作系统进行交流,才能发挥更大的威力。

1. Electron:Node.js 的丝滑体验

Electron最大的优势就是可以无缝使用Node.js的API。你可以直接在Vue应用中调用Node.js模块,访问文件系统、网络、系统信息等等。

  • 读取文件:

    // 在Vue组件中
    const fs = require('fs'); // 引入fs模块
    
    fs.readFile('path/to/your/file.txt', 'utf8', (err, data) => {
      if (err) {
        console.error(err);
        return;
      }
      console.log(data); // 文件内容
    });
  • 调用系统对话框:

    const { dialog } = require('electron').remote; // 引入dialog模块
    
    dialog.showOpenDialog({
      properties: ['openFile']
    }).then(result => {
      if (!result.canceled) {
        console.log(result.filePaths); // 用户选择的文件路径
      }
    }).catch(err => {
      console.log(err);
    });

2. Tauri:Command模式的优雅转身

Tauri的安全模型限制了直接访问系统API,你需要通过Command模式来暴露API给前端。

  • 定义Command (Rust):src-tauri/src/main.rs 文件中定义一个Command。

    use tauri::{command, State};
    
    #[derive(Default)]
    struct MyState {
        count: std::sync::Mutex<i32>,
    }
    
    #[command]
    fn my_custom_command(window: tauri::Window, state: State<'_, MyState>) -> Result<String, String> {
        let mut count = state.count.lock().unwrap();
        *count += 1;
    
        window.emit("my-event", format!("Command executed, count: {}", *count)).unwrap(); // 发送事件到前端
        Ok(format!("Hello from Rust! Count: {}", *count))
    }
    
    fn main() {
      tauri::Builder::default()
        .manage(MyState::default())
        .invoke_handler(tauri::generate_handler![my_custom_command])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
    }

    解释一下:

    • #[command] 宏将 my_custom_command 函数标记为一个可以从前端调用的Command。
    • window.emit 用于向前端发送事件。
    • State 用于管理应用的状态。
    • invoke_handler 将Command注册到Tauri应用中。
  • 调用Command (JavaScript): 在Vue组件中调用Command。

    // 在Vue组件中
    import { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';
    
    async function callCommand() {
      try {
        const result = await invoke('my_custom_command');
        console.log('Command result:', result);
      } catch (error) {
        console.error('Command error:', error);
      }
    }
    
    // 监听事件
    listen('my-event', event => {
      console.log('Received event:', event.payload);
    });

    解释一下:

    • invoke('my_custom_command') 调用Rust定义的Command。
    • listen('my-event', ...) 监听Rust发出的事件。
  • 访问文件系统 (Tauri): 访问文件系统需要使用 Tauri 提供的 API。

    #[command]
    fn read_file(path: String) -> Result<String, String> {
        match std::fs::read_to_string(path) {
            Ok(content) => Ok(content),
            Err(e) => Err(e.to_string()),
        }
    }
    import { invoke } from '@tauri-apps/api/tauri';
    
    async function readFile(filePath) {
        try {
            const content = await invoke('read_file', { path: filePath });
            console.log('File content:', content);
        } catch (error) {
            console.error('Error reading file:', error);
        }
    }

总结:

  • Electron 使用 Node.js API 更加直接和方便,但需要注意安全问题。
  • Tauri 使用 Command 模式更加安全,但需要编写 Rust 代码,学习成本稍高。

安全注意事项:

  • Electron: 避免直接在渲染进程中使用 nodeIntegration: truecontextIsolation: false。使用 contextBridge 来安全地暴露 Node.js API。对用户输入进行验证和过滤,防止XSS攻击。
  • Tauri: 配置严格的CSP (Content Security Policy),限制可以加载的资源。使用Tauri提供的安全API,避免直接访问系统资源。

最后的彩蛋:

无论你选择Electron还是Tauri,都要记得:

  • 代码规范: 保持代码整洁,注释清晰,方便维护。
  • 版本控制: 使用Git进行版本控制,方便回滚和协作。
  • 持续学习: Electron和Tauri都在不断发展,保持学习的热情,才能跟上时代的步伐。

好了,今天的讲座就到这里。希望大家都能把Vue应用成功地打包成功能强大的桌面应用,并在代码的世界里玩得开心! 如果大家有什么问题,欢迎提问。 咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注