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

各位观众老爷们,大家好!今天咱们开个小讲座,主题是“Vue 应用变身记:Tauri 和 Electron 助你打造桌面超能力”。 咱们要让 Vue 应用从网页小清新,摇身一变,成为功能强大的桌面应用,还能跟系统 API 亲密互动,想想是不是有点小激动?

开场白:网页到桌面,不止是换个壳

咱们都知道 Vue 是个前端框架,擅长构建用户界面。但浏览器毕竟有它的局限性,有些系统级别的操作,比如访问摄像头、操作文件系统、监听硬件事件,浏览器出于安全考虑,一般是不允许的。这时候,我们就需要 Tauri 和 Electron 这两个神器来帮忙了。

它们俩的作用,简单来说,就是给你的 Vue 应用套上一层“桌面外壳”,让它可以像普通的桌面应用一样运行,并且可以通过特定的 API,调用操作系统的功能。

第一幕:主角登场,Tauri vs. Electron

在开始“变身”之前,我们先来认识一下今天的主角:Tauri 和 Electron。

特性 Tauri Electron
底层技术 Rust + 系统 WebView Chromium + Node.js
包大小 非常小 (几 MB) 较大 (几十 MB)
性能 优秀,接近原生应用 相对较好,但资源占用较高
安全性 更安全,Rust 语言特性提供保障 相对安全,但需要注意安全漏洞
语言 主要使用 Rust,前端仍可用 HTML/CSS/JS 主要使用 JavaScript/HTML/CSS
开发难度 相对较高,需要学习 Rust 相对较低,JavaScript 开发者更容易上手
跨平台支持 支持 Windows, macOS, Linux 等 支持 Windows, macOS, Linux 等

简单总结一下:

  • Electron: 优点是上手快,JavaScript 开发者友好,生态系统完善。缺点是包体积大,资源占用高。
  • Tauri: 优点是包体积小,性能好,安全性高。缺点是学习曲线陡峭,需要学习 Rust。

所以,选择哪个,取决于你的项目需求和团队技术栈。如果你追求性能和安全,并且愿意学习 Rust,Tauri 是个不错的选择。如果你更看重开发效率和生态系统,Electron 可能更适合你。

第二幕:Tauri 初体验,Hello World!

咱们先来用 Tauri 创建一个简单的 Hello World 应用。

  1. 安装 Rust 和 Tauri CLI:

    首先,确保你的电脑上安装了 Rust。没有的话,去 Rust 官网下载安装:https://www.rust-lang.org/

    安装完成后,打开终端,安装 Tauri CLI:

    cargo install create-tauri-app
  2. 创建 Tauri 项目:

    cargo create-tauri-app

    这个命令会问你一些问题,比如项目名称、使用哪个前端框架等等。选择 Vue.js,然后一路回车就行。

  3. 运行 Tauri 应用:

    进入项目目录:

    cd <你的项目名称>

    运行开发模式:

    npm install # 安装依赖
    npm run tauri dev

    恭喜你,一个简单的 Tauri 应用就跑起来了!

  4. 修改前端代码:

    打开 src/App.vue 文件,修改内容,让它显示 "Hello World!"。

    <template>
      <div class="container">
        <h1>Hello World!</h1>
      </div>
    </template>
    
    <script setup>
    </script>
    
    <style scoped>
    .container {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    h1 {
      font-size: 3em;
      color: #42b983;
    }
    </style>

    保存文件,你会发现应用自动刷新,显示 "Hello World!"。

第三幕:Electron 入门,同样 Hello World!

接下来,咱们用 Electron 也创建一个 Hello World 应用。

  1. 创建 Electron 项目:

    创建一个新的目录,初始化 npm 项目:

    mkdir electron-hello-world
    cd electron-hello-world
    npm init -y
  2. 安装 Electron:

    npm install electron --save-dev
  3. 创建主进程文件 main.js:

    const { app, BrowserWindow } = require('electron');
    const path = require('path');
    
    function createWindow() {
      const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js'), // 预加载脚本,后面会讲到
          nodeIntegration: true, // 允许在渲染进程中使用 Node.js API
          contextIsolation: false // 关闭上下文隔离,方便使用 Node.js API
        }
      });
    
      mainWindow.loadFile('index.html'); // 加载 HTML 文件
      // mainWindow.webContents.openDevTools() // 打开开发者工具
    }
    
    app.whenReady().then(() => {
      createWindow();
    
      app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
      });
    });
    
    app.on('window-all-closed', function () {
      if (process.platform !== 'darwin') app.quit();
    });
  4. 创建预加载脚本 preload.js (可选):

    这个文件主要用于在渲染进程中安全地访问 Node.js API。 简单起见,这里可以先创建一个空文件,后面再详细介绍它的作用。

    // preload.js
  5. 创建 HTML 文件 index.html:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>
      </head>
      <body>
        <h1>Hello World!</h1>
      </body>
    </html>
  6. 配置 package.json:

    package.json 文件中添加一个 start 脚本:

    {
      "name": "electron-hello-world",
      "version": "1.0.0",
      "description": "",
      "main": "main.js",
      "scripts": {
        "start": "electron ."
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "electron": "^28.2.0"
      }
    }
  7. 运行 Electron 应用:

    npm start

    搞定!Electron 的 Hello World 应用也跑起来了。

第四幕:Vue + Tauri/Electron,强强联合

现在,咱们把 Vue 应用集成到 Tauri 和 Electron 中。

Tauri + Vue:

Tauri 项目默认已经集成了 Vue,所以你只需要把你的 Vue 应用代码复制到 src 目录下即可。然后修改 src-tauri/tauri.conf.json 文件,将 distDir 指向你的 Vue 应用的构建目录(通常是 dist)。

{
  "build": {
    "beforeBuildCommand": "npm run build", // 构建 Vue 应用的命令
    "beforeDevCommand": "npm run dev",   // 开发模式下运行 Vue 应用的命令
    "devPath": "http://localhost:3000",    // 开发模式下的 URL
    "distDir": "../dist"                // Vue 应用的构建目录
  },
  // ... 其他配置
}

在 Vue 项目中运行 npm run build 构建应用,然后运行 npm run tauri dev 启动 Tauri 应用。

Electron + Vue:

  1. 安装 Vue CLI:

    npm install -g @vue/cli
  2. 创建 Vue 项目:

    vue create vue-electron-app

    选择你喜欢的预设。

  3. 在 Electron 项目中集成 Vue 应用:

    将 Vue 应用的构建目录(dist)复制到 Electron 项目的根目录下。

    修改 main.js 文件,加载 Vue 应用的 index.html 文件:

    // main.js
    const { app, BrowserWindow } = require('electron');
    const path = require('path');
    
    function createWindow() {
      const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js'),
          nodeIntegration: true,
          contextIsolation: false
        }
      });
    
      mainWindow.loadFile(path.join(__dirname, 'dist/index.html')); // 加载 Vue 应用的 HTML 文件
      // mainWindow.webContents.openDevTools()
    }
    
    app.whenReady().then(() => {
      createWindow();
    
      app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
      });
    });
    
    app.on('window-all-closed', function () {
      if (process.platform !== 'darwin') app.quit();
    });

    在 Vue 项目中运行 npm run build 构建应用,然后运行 npm start 启动 Electron 应用。

第五幕:与原生 API 交互,释放桌面超能力

重头戏来了!咱们要让 Vue 应用与操作系统 API 交互。

Tauri:

Tauri 提供了一套强大的 API,可以通过 Rust 代码调用操作系统功能。

  1. 定义 Rust 函数:

    src-tauri/src/main.rs 文件中,定义一个 Rust 函数,用于读取文件内容:

    // src-tauri/src/main.rs
    use tauri::{Manager, State, AppHandle, Window};
    use std::fs;
    
    #[tauri::command]
    async fn read_file(path: String) -> Result<String, String> {
      match fs::read_to_string(path) {
        Ok(content) => Ok(content),
        Err(e) => Err(e.to_string()),
      }
    }
    
    fn main() {
      tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![read_file]) // 注册 Rust 函数
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
    }
  2. 在 Vue 代码中调用 Rust 函数:

    在 Vue 组件中,使用 invoke 函数调用 Rust 函数:

    // src/App.vue
    <template>
      <div>
        <button @click="readFile">读取文件</button>
        <p>{{ fileContent }}</p>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    import { invoke } from '@tauri-apps/api/tauri';
    
    const fileContent = ref('');
    
    const readFile = async () => {
      try {
        const content = await invoke('read_file', { path: '/path/to/your/file.txt' });
        fileContent.value = content;
      } catch (error) {
        console.error(error);
      }
    };
    </script>

Electron:

Electron 可以通过 nodeIntegrationcontextIsolation 两种方式访问 Node.js API。

  • nodeIntegration: true, contextIsolation: false: 这种方式最简单,可以直接在渲染进程中使用 require 函数引入 Node.js 模块。但是安全性较低,不推荐在生产环境中使用。

  • nodeIntegration: false, contextIsolation: true: 这种方式更安全,需要在主进程中定义 API,然后通过 preload.js 脚本暴露给渲染进程。

咱们以更安全的 contextIsolation: true 方式为例:

  1. 在主进程中定义 API:

    // main.js
    const { app, BrowserWindow, ipcMain } = require('electron');
    const path = require('path');
    const fs = require('fs');
    
    function createWindow() {
      const mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js'),
          nodeIntegration: false,
          contextIsolation: true
        }
      });
    
      mainWindow.loadFile(path.join(__dirname, 'dist/index.html'));
      // mainWindow.webContents.openDevTools()
    }
    
    app.whenReady().then(() => {
      createWindow();
    
      app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
      });
    });
    
    app.on('window-all-closed', function () {
      if (process.platform !== 'darwin') app.quit();
    });
    
    ipcMain.handle('read-file', async (event, path) => {
      try {
        const content = fs.readFileSync(path, 'utf-8');
        return content;
      } catch (error) {
        console.error(error);
        return error.message;
      }
    });
  2. preload.js 中暴露 API:

    // preload.js
    const { contextBridge, ipcRenderer } = require('electron');
    
    contextBridge.exposeAPI({
      readFile: async (path) => {
        return await ipcRenderer.invoke('read-file', path);
      }
    });
  3. 在 Vue 代码中调用 API:

    // src/App.vue
    <template>
      <div>
        <button @click="readFile">读取文件</button>
        <p>{{ fileContent }}</p>
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue';
    
    const fileContent = ref('');
    
    const readFile = async () => {
      try {
        const content = await window.readFile('/path/to/your/file.txt');
        fileContent.value = content;
      } catch (error) {
        console.error(error);
      }
    };
    </script>

第六幕:打包发布,让世界看到你的作品

最后,咱们要把应用打包成可执行文件,让大家都能使用。

Tauri:

npm run tauri build

打包后的文件会在 src-tauri/target/release/bundle 目录下。

Electron:

需要使用 electron-builderelectron-packager 等工具进行打包。

  1. 安装 electron-builder:

    npm install electron-builder --save-dev
  2. 配置 package.json:

    {
      "name": "electron-hello-world",
      "version": "1.0.0",
      "description": "",
      "main": "main.js",
      "scripts": {
        "start": "electron .",
        "build": "vue-cli-service build",
        "electron:build": "vue-cli-service electron:build",
        "electron:serve": "vue-cli-service electron:serve",
        "postinstall": "electron-builder install-app-deps",
        "pack": "electron-builder --dir",
        "dist": "electron-builder"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "electron": "^28.2.0",
        "electron-builder": "^24.9.1",
        "@vue/cli-plugin-electron-builder": "~5.0.0",
        "@vue/cli-service": "~5.0.0"
      },
      "build": {
        "appId": "com.example.app",
        "productName": "MyApp",
        "copyright": "Copyright © 2024 Example",
        "directories": {
          "output": "dist_electron"
        },
        "files": [
          "dist/**/*",
          "main.js",
          "preload.js"
        ],
        "asar": true,
        "mac": {
          "category": "public.app-category.utilities"
        },
        "win": {
          "target": [
            "nsis"
          ]
        },
        "linux": {
          "target": [
            "deb",
            "rpm",
            "snap"
          ]
        }
      }
    }
  3. 打包应用:

    npm run dist

    打包后的文件会在 dist_electron 目录下。

总结:Vue 应用的无限可能

通过 Tauri 和 Electron,我们可以将 Vue 应用打包成功能强大的桌面应用,并且可以与操作系统 API 交互,实现更多更强大的功能。 无论你选择哪个框架,都要记住安全第一,避免滥用 Node.js API,确保你的应用安全可靠。

希望这次讲座能帮助你更好地理解 Tauri 和 Electron,让你在 Vue 应用的开发道路上更上一层楼。 感谢大家的观看!

发表回复

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