Tauri介绍

引用自官网

Tauri 是一个构建适用于所有主流桌面和移动平台的轻快二进制文件的框架。开发者们可以集成任何用于创建用户界面的可以被编译成 HTML、JavaScript 和 CSS 的前端框架,同时可以在必要时使用 Rust、Swift 和 Kotlin 等语言编写后端逻辑。

对于开发者而言,Tauri 有三个主要优势:

  • 构建应用所需的可靠基础(利用 Rust 提供的内存、线程和类型安全方面的优势)
  • 使用系统原生 webview(网页视图)带来的更小打包体积,不需要在每个应用中都打包一个浏览器引擎
  • 使用任何前端技术和多种语言绑定带来的灵活性

环境准备

Tauri前置要求

我开发的是Windows的桌面程序,这里只讲下Windows的要求,其它系统可以查看

1. 安装Microsoft C++ 构建工具

Tauri 使用 Microsoft C++ 构建工具进行开发以及 Microsoft Edge WebView2。这些都是在 Windows 上进行开发所必需的。

  1. 去windows 官网下载Microsoft C++ 生成工具安装程序并打开它以开始安装。
  2. 在安装过程中,选中“使用 C++ 进行桌面开发”选项。

2. WebView2

WebView 2 已安装在 Windows 10(从版本 1803 开始)和更高版本的 Windows 上。我的电脑是win10的,所以这一步可以跳过。

如果有需要的话,可以通过访问 WebView2 Runtime 下载区 安装 WebView2。下载“常青版独立安装程序(Evergreen Boostrapper)”并安装它。

3. Rust

在安装这个之前,如果没有安装第一步的Microsoft C++ 构建工具会有提示。尽量按顺序安装。

Tauri 使用 Rust 构建并需要它进行开发。可以在 www.rust-lang.org/tools/insta… 下载 rustup。下载之后双击运行,一路下一步就行。

至此系统环境就准备好了。接下来要在项目里面安装Tauri依赖了。

NPM依赖安装

  1. 在项目内安装tauri-apps/cli

 转到项目根文件夹执行下面指令

pnpm install @tauri-apps/cli@latest

执行指令如图

安装成功如下图

使用tauri V2 初始化项目

在项目根目录运行:

pnpm tauri init

这些配置都可以直接先回车,后续在配置文件tauri.conf.json中还可以手动修改。

配置解读

// 应用名称,它默认会去读package.json里面的name,不改的话可以直接回车
✔ What is your app name? tauri-app
// 应用窗口上的名称,默认也是name,按自己需求修改
✔ What should the window title be? tauri-app
// Vite打包后的静态资源位置,默认../dist,可以直接回车
✔ Where are your web assets located? ..
// 本地启动Vite开发服务url
✔ What is the url of your dev server? http://localhost:5173
// 启动前端开发环境命令,默认 pnpm run dev
✔ What is your frontend dev command? pnpm run dev
// 前端构建命令,默认npm run build
✔ What is your frontend build command? pnpm run build:dev

最后两项要确认正确,在tauri启动开发服务和打包前,是会先运行Vue项目的对应命令的。

新建配置文件

在项目根目录下,新建.env.win文件,用于window平台的配置文件

结构如下图

具体内容如下

# 环境
VITE_NODE_ENV=production

# 接口前缀
VITE_API_BASE_PATH=http://{你的后端IP地址}:9200

# 打包路径
VITE_BASE_PATH=/dist-win/

# 是否删除debugger
VITE_DROP_DEBUGGER=false

# 是否删除console.log
VITE_DROP_CONSOLE=false

# 是否sourcemap
VITE_SOURCEMAP=true

# 输出路径
VITE_OUT_DIR=dist-win

# 标题
VITE_APP_TITLE=二次开发测试

# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false

# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false

# 是否开启mock
VITE_USE_MOCK=true

# 是否切割css
VITE_USE_CSS_SPLIT=true

# 是否使用在线图标
VITE_USE_ONLINE_ICON=true

# 是否隐藏全局设置按钮
VITE_HIDE_GLOBAL_SETTING=false

现在讨论使用http协议访问后端,https协议的使用会在后面文章详细介绍

修改package.json编译脚本

在package.json编译脚本中增加.env.win环境的编译指令,只需要增加build:win 和serve:win指令

编译脚本修改如如下所示

{
  "name": "vue-element-plus-admin",
  "version": "2.9.0",
  "description": "一套基于vue3、element-plus、typesScript、vite4的后台集成方案。",
  "author": "Archer <502431556@qq.com>",
  "private": false,
  "type": "module",
  "scripts": {
    "i": "pnpm install",
    "dev": "pnpm vite --mode base",
    "ts:check": "pnpm vue-tsc --noEmit --skipLibCheck",
    "build:pro": "pnpm vite build --mode pro",
    "build:gitee": "pnpm vite build --mode gitee",
    "build:dev": "pnpm vite build --mode dev",
    "build:test": "pnpm vite build --mode test",
    "build:win": "pnpm vite build --mode win",
    "serve:pro": "pnpm vite preview --mode pro",
    "serve:dev": "pnpm vite preview --mode dev",
    "serve:test": "pnpm vite preview --mode test",
    "serve:win": "pnpm vite preview --mode win",
    "npm:check": "pnpx npm-check-updates -u",
    "clean": "pnpx rimraf node_modules",
    "clean:cache": "pnpx rimraf node_modules/.cache",
    "lint:eslint": "eslint . --fix \"src/**/*.{js,ts,tsx,vue,html}\"",
    "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
    "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
    "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs",
    "prepare": "husky install",
    "p": "plop",
    "icon": "esno ./scripts/icon.ts"
  },
  "dependencies": {
    "@iconify/iconify": "^3.1.1",
    "@iconify/vue": "^4.3.0",
    "@tauri-apps/api": "^2.8.0",
    "@tauri-apps/cli": "^2.8.4",
    "@tauri-apps/plugin-http": "~2.3.0",
    "@vueuse/core": "^12.8.2",
    "@wangeditor/editor": "^5.1.23",
    "@wangeditor/editor-for-vue": "^5.1.12",
    "@zxcvbn-ts/core": "^3.0.4",
    "animate.css": "^4.1.1",
    "axios": "^1.12.2",
    "axios-tauri-api-adapter": "^2.0.6",
    "cropperjs": "^1.6.2",
    "dayjs": "^1.11.18",
    "driver.js": "^1.3.6",
    "echarts": "^5.6.0",
    "echarts-wordcloud": "^2.1.0",
    "element-plus": "2.9.2",
    "lodash-es": "^4.17.21",
    "mitt": "^3.0.1",
    "monaco-editor": "^0.52.2",
    "nprogress": "^0.2.0",
    "pinia": "^2.3.1",
    "pinia-plugin-persistedstate": "^4.5.0",
    "qrcode": "^1.5.4",
    "qs": "^6.14.0",
    "url": "^0.11.4",
    "vue": "3.5.13",
    "vue-draggable-plus": "^0.6.0",
    "vue-i18n": "11.0.1",
    "vue-json-pretty": "^2.5.0",
    "vue-router": "^4.5.1",
    "vue-types": "^5.1.3",
    "xgplayer": "^3.0.23"
  },
  "devDependencies": {
    "@commitlint/cli": "^19.8.1",
    "@commitlint/config-conventional": "^19.8.1",
    "@iconify/json": "^2.2.390",
    "@intlify/unplugin-vue-i18n": "^6.0.8",
    "@types/fs-extra": "^11.0.4",
    "@types/inquirer": "^9.0.9",
    "@types/lodash-es": "^4.17.12",
    "@types/mockjs": "^1.0.10",
    "@types/node": "^22.18.7",
    "@types/nprogress": "^0.2.3",
    "@types/qrcode": "^1.5.5",
    "@types/qs": "^6.14.0",
    "@types/sortablejs": "^1.15.8",
    "@typescript-eslint/eslint-plugin": "^8.45.0",
    "@typescript-eslint/parser": "^8.45.0",
    "@unocss/transformer-variant-group": "^0.65.4",
    "@vitejs/plugin-legacy": "^6.1.1",
    "@vitejs/plugin-vue": "^5.2.4",
    "@vitejs/plugin-vue-jsx": "^4.2.0",
    "autoprefixer": "^10.4.21",
    "chalk": "^5.6.2",
    "consola": "^3.4.2",
    "eslint": "^9.36.0",
    "eslint-config-prettier": "^9.1.2",
    "eslint-define-config": "^2.1.0",
    "eslint-plugin-prettier": "^5.5.4",
    "eslint-plugin-vue": "^9.33.0",
    "esno": "^4.8.0",
    "fs-extra": "^11.3.2",
    "husky": "^9.1.7",
    "inquirer": "^12.9.6",
    "less": "^4.4.1",
    "lint-staged": "^15.5.2",
    "mockjs": "^1.1.0",
    "plop": "^4.0.4",
    "postcss": "^8.5.6",
    "postcss-html": "^1.8.0",
    "postcss-less": "^6.0.0",
    "prettier": "^3.6.2",
    "rimraf": "^6.0.1",
    "rollup": "^4.52.3",
    "rollup-plugin-visualizer": "^5.14.0",
    "stylelint": "^16.24.0",
    "stylelint-config-html": "^1.1.0",
    "stylelint-config-recommended": "^14.0.1",
    "stylelint-config-standard": "^36.0.1",
    "stylelint-order": "^6.0.4",
    "terser": "^5.44.0",
    "typescript": "5.7.3",
    "typescript-eslint": "^8.45.0",
    "unocss": "^0.65.4",
    "vite": "6.0.7",
    "vite-plugin-ejs": "^1.7.0",
    "vite-plugin-eslint": "^1.8.1",
    "vite-plugin-mock": "2.9.6",
    "vite-plugin-progress": "^0.0.7",
    "vite-plugin-purge-icons": "^0.10.0",
    "vite-plugin-style-import": "2.0.0",
    "vite-plugin-svg-icons": "^2.0.1",
    "vite-plugin-url-copy": "^1.1.4",
    "vue-tsc": "^2.2.12"
  },
  "packageManager": "pnpm@9.15.3",
  "engines": {
    "node": ">=18.0.0",
    "pnpm": ">=8.1.0"
  },
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/kailong321200875/vue-element-plus-admin.git"
  },
  "bugs": {
    "url": "https://github.com/kailong321200875/vue-element-plus-admin/issues"
  },
  "homepage": "https://github.com/kailong321200875/vue-element-plus-admin"
}

修改Vite.config.js配置

下面的配置可以根据自己项目配置情况添加。

import { resolve } from 'path'
import { loadEnv } from 'vite'
import type { UserConfig, ConfigEnv } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import progress from 'vite-plugin-progress'
import EslintPlugin from 'vite-plugin-eslint'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
import { viteMockServe } from 'vite-plugin-mock'
import PurgeIcons from 'vite-plugin-purge-icons'
import ServerUrlCopy from 'vite-plugin-url-copy'
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'
import UnoCSS from 'unocss/vite'
import { visualizer } from 'rollup-plugin-visualizer'

// https://vitejs.dev/config/
const root = process.cwd()

function pathResolve(dir: string) {
  return resolve(root, '.', dir)
}

export default ({ command, mode }: ConfigEnv): UserConfig => {
  let env = {} as any
  const isBuild = command === 'build'
  if (!isBuild) {
    env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root)
  } else {
    env = loadEnv(mode, root)
  }
  return {
    base:  env.VITE_NODE_ENV=== 'production'? './' : env.VITE_BASE_PATH,
    plugins: [
      Vue({
        script: {
          // 开启defineModel
          defineModel: true
        }
      }),
      VueJsx(),
      ServerUrlCopy(),
      progress(),
      env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'false'
        ? createStyleImportPlugin({
            resolves: [ElementPlusResolve()],
            libs: [
              {
                libraryName: 'element-plus',
                esModule: true,
                resolveStyle: (name) => {
                  if (name === 'click-outside') {
                    return ''
                  }
                  return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
                }
              }
            ]
          })
        : undefined,
      EslintPlugin({
        cache: false,
        failOnWarning: false,
        failOnError: false,
        include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
      }),
      VueI18nPlugin({
        runtimeOnly: true,
        compositionOnly: true,
        include: [resolve(__dirname, 'src/locales/**')]
      }),
      createSvgIconsPlugin({
        iconDirs: [pathResolve('src/assets/svgs')],
        symbolId: 'icon-[dir]-[name]',
        svgoOptions: true
      }),
      PurgeIcons(),
      env.VITE_USE_MOCK === 'true'
        ? viteMockServe({
            ignore: /^\_/,
            mockPath: 'mock',
            localEnabled: !isBuild,
            prodEnabled: isBuild,
            injectCode: `
          import { setupProdMockServer } from '../mock/_createProductionServer'

          setupProdMockServer()
          `
          })
        : undefined,
      ViteEjsPlugin({
        title: env.VITE_APP_TITLE
      }),
      UnoCSS()
    ],

    css: {
      preprocessorOptions: {
        less: {
          additionalData: '@import "./src/styles/variables.module.less";',
          javascriptEnabled: true
        }
      }
    },
    resolve: {
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.less', '.css'],
      alias: [
        {
          find: 'vue-i18n',
          replacement: 'vue-i18n/dist/vue-i18n.cjs.js'
        },
        {
          find: /\@\//,
          replacement: `${pathResolve('src')}/`
        }
      ]
    },
    esbuild: {
      pure: env.VITE_DROP_CONSOLE === 'true' ? ['console.log'] : undefined,
      drop: env.VITE_DROP_DEBUGGER === 'true' ? ['debugger'] : undefined
    },


    // 防止 Vite 清除 Rust 显示的错误
    clearScreen: false,
    server: {
      port: 9081,
      proxy: {
        // 选项写法
        '/api': {
          target: 'http://127.0.0.1:8000',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        },
        '/config/console':{
          target:'http://127.0.0.1:9200',
          changeOrigin:true,
          secure: false //不验证https证书
        }
      },
      hmr: {
        overlay: false
      },
      // Tauri 工作于固定端口,如果端口不可用则报错
      strictPort: true,
      host: 'localhost'
    },
      // 添加有关当前构建目标的额外前缀,使这些 CLI 设置的 Tauri 环境变量可以在客户端代码中访问
    envPrefix: ['VITE_', 'TAURI_ENV_*'],
    build: {
      target: process.env.TAURI_ENV_PLATFORM == 'windows'? 'chrome105': process.env.TAURI_ENV_PLATFORM == 'mac'? 'safari13':'es2015',
      outDir: env.VITE_OUT_DIR || 'dist',
      sourcemap: env.VITE_SOURCEMAP === 'true',
      // brotliSize: false,
      rollupOptions: {
        plugins: env.VITE_USE_BUNDLE_ANALYZER === 'true' ? [visualizer()] : undefined,
        // 拆包
        output: {
          manualChunks: {
            'vue-chunks': ['vue', 'vue-router', 'pinia', 'vue-i18n'],
            'element-plus': ['element-plus'],
            'wang-editor': ['@wangeditor/editor', '@wangeditor/editor-for-vue'],
            echarts: ['echarts', 'echarts-wordcloud']
          }
        }
      },
      cssCodeSplit: !(env.VITE_USE_CSS_SPLIT === 'false'),
      cssTarget: ['chrome31']
    },
    
    optimizeDeps: {
      include: [
        'vue',
        'vue-router',
        'vue-types',
        'element-plus/es/locale/lang/zh-cn',
        'element-plus/es/locale/lang/en',
        '@iconify/iconify',
        '@vueuse/core',
        'axios',
        'qs',
        'echarts',
        'echarts-wordcloud',
        'qrcode',
        '@wangeditor/editor',
        '@wangeditor/editor-for-vue',
        'vue-json-pretty',
        '@zxcvbn-ts/core',
        'dayjs',
        'cropperjs'
      ]
    }
  }
}

修改配置文件tauri.conf.json

初始化完成后,会自动生成src-tauri的目录,配置文件tauri.conf.json就在其中。

配置文件如下

{
  "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
  "productName": "vue-element-plus-admin",
  "version": "0.1.0",
  "identifier": "com.tauri.dev",
  "build": {
    "frontendDist": "../dist-dev",
    "devUrl": "http://localhost:8080",
    "beforeDevCommand": "pnpm run dev",
    "beforeBuildCommand": "pnpm run buil:dev"
  },
  "app": {
    "windows": [
      {
        "title": "vue-element-plus-admin",
        "width": 800,
        "height": 600,
        "resizable": true,
        "fullscreen": false
      }
    ],
    "security": {
      "csp": null
    }
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}

我们需要修改的项目有

  "identifier": "com.test.next.dev" : 修改成自己的唯一表示
   "frontendDist": "../dist", 这里要根据配置文件.env.**配置的输出,等编译项目的时候使用,由于我们选择是配置项目 "beforeBuildCommand": "pnpm run build:win",所以我们根据.env.dev文件中的配置项:VITE_OUT_DIR=dist-win 需要配置为    "frontendDist": "../dist-win"

"devUrl": "http://localhost:9081", 这个是根据vite.config.ts文件里的本地启动端口设置的,因为我们是定在了9081,所以要保持一致

"beforeDevCommand": "pnpm run dev", 开发阶段启动先启动的vite项目指令 

"beforeBuildCommand": "pnpm run build:win"编译打包的时候先运行的vite项目指令

整个修改完的配置文件如下

{
  "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
  "productName": "vue-element-admin",
  "version": "0.1.0",
  "identifier": "com.test.next.dev",
  "build": {
    "frontendDist": "../dist-win",
    "devUrl": "http://localhost:9081",
    "beforeDevCommand": "pnpm run dev",
    "beforeBuildCommand": "pnpm run build:win"
  },
  "app": {
    "windows": [
      {
        "title": "vue-element-admin",
        "width": 800,
        "height": 600,
        "resizable": true,
        "fullscreen": false
      }
    ],
    "security": {
      "csp": null
    }
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "windows": {
        "wix": {
          "language": "zh-CN"
        },
        "nsis": {
          "languages": ["SimpChinese", "English"],
          "displayLanguageSelector": true
        }
    },
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}

上文中bundle下的配置项如下,是用于配置安装过程的国际化的,具体可以根据自己的语言需要配置,这里配置了简体中文

  "windows": {
        "wix": {
          "language": "zh-CN"
        },
        "nsis": {
          "languages": ["SimpChinese", "English"],
          "displayLanguageSelector": true
        }
    },

修改tauri v2权限

修改src-tauri/capabilities/default.json如下所示

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "enables the default permissions",
  "windows": [
    "main"
  ],
  "permissions": [
    "core:default",
    "log:default",
    {
      "identifier": "http:default",
      "allow": [
        {
          "url": "https://*:*"
        },
        {
          "url": "http://*:*"
        }
      ]
    }
    
  ],
  "platforms": [
    "windows",
    "linux",
    "macOS"
  ]
}

注意:在实际项目中为了安全请根据自己的情况进行在allow的url里设置,我们现在的只是测试环境所以设置了全地址和全端口访问都可以

开发环境启动

先启动后端程序,并设置web服务在地址 https://{你的IP地址}:9200

在项目根目录执行命令如下

pnpm tauri dev

启动后,你就会看到启动一个window窗口如下图所示

输入用户名admin和密码admin 点击登录将进入首页如下图所示

发布windows 安装包

添加axios-tauri-api-adapter

因为本项目使用的axios作为网络通信工具,axios默认使用的nodejs的后端通信接口,但是tauri 2 使用的fetch作为API接口,我们需要使用一个适配rust fetch API,我们使用的包是 axios-tauri-api-adapter 最新版本是2.0.6,也是我们使用的版本

在项目根文件执行指令

 pnpm install axios-tauri-api-adapter

修改文件src/axios/service.ts文件加入axios adapter,如下图所示因为开发阶段使用vite,所以在开发阶段和编译打包web程序的时候需要把其注释掉

在tauri v2中添加tauri-plugin-http模块依赖

note:一定要选择2.3.0版本,大于其版本和 axios-tauri-api-adapter冲突

在项目根目录下执行下面指令

pnpm tauri add http@2.3.0

修改依赖版本

修改文件src-tauri/Cargo.toml

在末尾加上 tauri-plugin-http = "=2.3.0" 配置项

如下所示

[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.77.2"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies]
tauri-build = { version = "2.4.1", features = [] }

[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
log = "0.4"
tauri = { version = "2.8.5", features = [] }
tauri-plugin-log = "2"
tauri-plugin-http = "=2.3.0"

在src-tauri文件执行下面指令以指定tauri-plugin-http 版本

cargo update 

在根目录下执行编译打包windows 安装包命令

pnpm tauri build

编译成功后将有如下日志

我们看到生成的安装包在文件夹下C:\Users\tiger\content\vue3\element\vue-element-plus-admin\src-tauri\target\release\bundle下,其中msi时msi安装包,nsis时exe安装包如下图所示

msi安装包

exe安装包

错误处理

当出现下面错误的时候,只需要在vscode中把vue-element-plus-admin\src-tauri\target文件夹删除,重新运行就可以,这是由于权限引起无法访问target下面的文件及子文件夹下文件造成的

安装程序

我们只演示setup.exe类型的安装过程

1.双击setup.exe类型的安装程序,选择语言

进入中文安装向导

下面的过程依据自己的安装位置进行安装

安装完成

设置桌面快捷方式,点击完成安装完成

点击完成后就可以启动安装的windows程序了

输入用户名密码就可以登录了,程序界面大小可以自由调节

模拟后端的程序地址:https://gitee.com/tigershi123/finance-back-parent

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐