Для чего нужна данная статья? :
- Получить представление о написание мобильного приложения с Vue3 и Rust.
- Найти компромиссы между использованием собственных библиотек Vue3 и WebAssembly.
Зачем Вам это уметь? :
1. Веб-приложение с Rust (Backend) + Vue 3 (Frontend)
Классический вариант, где Rust используется для серверной логики, а Vue 3 — для клиентской части:
- Actix Web / Axum + Vue 3 → Rust обрабатывает API-запросы, а Vue 3 использует их для отображения данных.
- Warp + Vue 3 → Легковесный сервер с WebSockets для real-time приложений.
- Rocket + Vue 3 → Удобный фреймворк для REST API.
💡 Дополнительно:
- Использование Diesel / SQLx для работы с БД.
- gRPC (tonic) вместо REST для более быстрой коммуникации.
2. Статический сайт (Wasm) с Rust и Vue 3
Если нужно создать сайт, где вычисления выполняются в браузере, можно использовать Rust + WebAssembly (Wasm):
- Leptos / Sycamore + Vue 3 → Rust рендерит часть компонентов.
- Yew + Vue 3 → Rust-компоненты внутри Vue.
- Wasm-bindgen + Vue 3 → Rust выполняет тяжелые вычисления в браузере (например, обработку изображений или криптографию).
💡 Пример:
Vue 3 управляет UI, а Rust через Wasm обрабатывает данные (например, сложные алгоритмы в веб-приложении).
3. Десктопное приложение на Rust + Vue 3
Можно создать кроссплатформенное приложение с графическим интерфейсом:
- Tauri + Vue 3 → Rust управляет логикой и API, а Vue 3 рисует интерфейс.
- Dioxus + Vue 3 → Использование WebView для Vue + нативные элементы через Dioxus.
- wry + Vue 3 → Минималистичный вариант WebView + Rust.
💡 Применение:
- Десктопные приложения (менеджеры паролей, крипто-кошельки).
- Редакторы изображений / аудио / видео.
- Локальные утилиты с UI.
4. Встраивание Rust в Vue 3 через WebAssembly
Если у вас уже есть Vue 3 приложение, но нужно ускорить отдельные вычисления:
- wasm-pack + Vue 3 → Вызов функций Rust из Vue через WebAssembly.
- Wasm Worker + Vue 3 → Rust выполняет фоновые задачи.
- Serde + JSON → Rust обрабатывает данные и передает в Vue.
💡 Применение:
- Обработка больших данных (Big Data).
- Криптография (шифрование, хеширование).
- Физические симуляции / 3D-рендеринг.
5. Серверная отрисовка Vue 3 с Rust (SSR)
Vue 3 поддерживает SSR (Server-Side Rendering), а Rust может генерировать HTML на сервере:
- Actix Web + Vue SSR → Генерация страниц на сервере.
- Axum + Vue 3 + HTMX → Гибридный подход с частичной серверной отрисовкой.
- Rust + Nuxt 3 → Nuxt использует Rust API для предзагрузки данных.
💡 Применение:
- Быстрые SEO-оптимизированные сайты.
- SSR без необходимости в Node.js.
6. Fullstack-решения (Rust + Vue 3 + GraphQL)
Vue 3 может работать с Rust GraphQL-сервером:
- async-graphql + Vue 3 → Высокопроизводительный GraphQL-сервер.
- Juniper + Vue 3 → Интеграция GraphQL с Diesel.
- Vue Apollo + Rust GraphQL → Frontend + backend взаимодействие.
💡 Применение:
- Реактивные и real-time приложения.
- Оптимизация запросов к API.
Настройте среду разработки: Node.js и npm для запуска Vue3.
Создайте новый проект Vue3 с помощью интерфейса командной строки Vue:
Выполните команду npm install -g @vue/cli для глобальной установки интерфейса командной строки Vue.
Выполните команду vue create my-app, чтобы создать новый проект Vue3.
Следуйте инструкциям на экране, чтобы настроить параметры проекта.
Добавьте необходимые зависимости в проект Vue3:
Установите плагин vue-rust, выполнив команду npm install vue-rust --save
Установите плагин vue-router, выполнив команду npm install vue-router --save.
Постройте rust-код: создайте проект Rust с помощью Cargo
Пример:
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;
#[derive(Deserialize)] struct Query {
amount: f64,
from: String,
to: String,
}
#[get("/api/convert")] async fn convert(query: web::Query<Query>) -> impl Responder {
let exchange_rate = get_exchange_rate(&query.from, &query.to).await;
let result = query.amount * exchange_rate;
HttpResponse::Ok().json(result)
}
async fn get_exchange_rate(from: &str, to: &str) -> f64 {
}
#[actix_web::main] async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(convert))
.bind("127.0.0.1:8080")?
.run()
.await
}
Скопируйте скомпилированный код Rust в общую папку проекта Vue3
Создайте компоненты Vue3:
Используйте плагин vue-rust для вызова кода Rust из компонентов Vue3
Используйте подключаемый модуль vue-router для создания маршрутов для своего приложения.
Пример:
<template>
<div>
<input v-model="amount" type="number" placeholder="Enter amount" />
<select v-model="fromCurrency">
<option v-for="currency in currencies" :key="currency">
{{ currency }}
</option>
</select>
<select v-model="toCurrency">
<option v-for="currency in currencies" :key="currency">
{{ currency }}
</option>
</select>
<button @click="convert">Convert</button>
<p>Result: {{ result }}</p> </div>
</template>
<script>
export default {
data() {
return {
amount: null,
fromCurrency: null,
toCurrency: null,
result: null,
currencies: ['USD', 'EUR', 'GBP', 'JPY']
}
},
methods: {
async convert() {
const response = await fetch(`/api/convert?amount=${this.amount}
&from=${this.fromCurrency}&to=${this.toCurrency}`);
const data = await response.json();
this.result = data.result;
}
}
}
</script>
Постройте и запустите приложение:
Выполните команду npm run build для построения приложения Vue3.
Выполнение команды npm run serve для запуска приложения на локальном сервере
Используйте такие инструменты, как Capacitor или Cordova, для создания и развертывания приложения на устройствах iOS и Android.
Пример модификации созданного выше приложения с использованием Ionic:
npm install -g @ionic/cli
ionic start my-app
Чтобы сделать приложение доступным на мобильных устройствах, выполните:
ionic cap add ios
ionic cap add android
ionic run build
npx cap open android
Интегрируйте Vue3 с Ionic: после создания приложения Vue вы можете интегрировать его с приложением Ionic. Для этого вам нужно будет установить пакет @ionic/vue, а затем обновить файл main.js для импорта и использования компонентов Ionic. Выполните следующие команды для установки пакета:
npm install @ionic/vue @ionic/vue-router
Как добавить Rust в проект?: нужно установить цепочку инструментов Rust и создать новый проект Rust. Выполните следующие команды, чтобы установить цепочку инструментов Rust и создать новый проект Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs
shrustup target add wasm32-unknown-unknown
cargo new my-rust-app --lib
Сборка Rust как WebAssembly: чтобы использовать Rust в приложении Vue, вам нужно будет создать его как WebAssembly. Для этого необходимо добавить ящики wasm-bindgen и js-sys в проект Rust, а затем обновить.
Backend на Rust:
✅ REST API (Axum) → Основной интерфейс.
✅ gRPC API (tonic) → Быстрая коммуникация.
✅ GraphQL API (async-graphql) → Гибкость запросов.
✅ ML-инференс (ONNX, candle) → Запуск моделей.
✅ PostgreSQL / SurrealDB → Хранение данных.
Добавим зависимости в Cargo.toml:
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
tonic = "0.11"
prost = "0.12"
async-graphql = "7"
async-graphql-axum = "7"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] }
candle-core = "0.3" # Для ML
candle-onnx = "0.3" # Для ONNX моделей
serde = { version = "1", features = ["derive"] }
dotenv = "0.15"
Создадим src/main.rs:
use axum::{routing::get, Router};
use std::net::SocketAddr;
async fn health_check() -> &'static str {
"ML Backend is running!"
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(health_check));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Server running at http://{}", addr);
axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();
}
Создадим GraphQL-сервер:
use async_graphql::{Schema, Object, EmptyMutation, EmptySubscription};
use async_graphql_axum::{GraphQLSchema, GraphQLRequest, GraphQLResponse};
struct QueryRoot;
#[Object]
impl QueryRoot {
async fn hello(&self) -> &str {
"Hello from GraphQL!"
}
}
pub type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
async fn graphql_handler(schema: GraphQLSchema<MySchema>, req: GraphQLRequest) -> GraphQLResponse {
schema.execute(req.into_inner()).await.into()
}
#[tokio::main]
async fn main() {
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish();
let app = Router::new()
.route("/", get(health_check))
.route("/graphql", axum::routing::post(graphql_handler))
.layer(axum::Extension(schema));
axum::Server::bind(&([127, 0, 0, 1], 3000).into())
.serve(app.into_make_service())
.await
.unwrap();
}
Создадим proto/ml.proto:
syntax = "proto3";
package ml;
service MLService {
rpc Predict (PredictRequest) returns (PredictResponse);
}
message PredictRequest {
repeated float input = 1;
}
message PredictResponse {
repeated float output = 1;
}
Сгенерируем код:
tonic-build::compile_protos("proto/ml.proto").unwrap();
Добавим gRPC-сервер:
use tonic::{transport::Server, Request, Response, Status};
use ml::ml_service_server::{MlService, MlServiceServer};
use ml::{PredictRequest, PredictResponse};
pub mod ml {
tonic::include_proto!("ml");
}
#[derive(Default)]
pub struct MyMLService;
#[tonic::async_trait]
impl MlService for MyMLService {
async fn predict(
&self,
request: Request<PredictRequest>,
) -> Result<Response<PredictResponse>, Status> {
let input = request.into_inner().input;
let output = input.iter().map(|x| x * 2.0).collect();
Ok(Response::new(PredictResponse { output }))
}
}
#[tokio::main]
async fn main() {
let addr = "[::1]:50051".parse().unwrap();
let ml_service = MyMLService::default();
println!("gRPC Server listening on {}", addr);
Server::builder()
.add_service(MlServiceServer::new(ml_service))
.serve(addr)
.await
.unwrap();
}
Интеграция ML (ONNX)
use candle_core::{Tensor, DType};
use candle_onnx::Model;
fn load_model() -> Model {
Model::from_path("model.onnx").unwrap()
}
fn run_inference(model: &Model, input: Vec<f32>) -> Vec<f32> {
let tensor = Tensor::from_vec(input, &[1, 3]).unwrap();
let output = model.run(&tensor).unwrap();
output.to_vec1().unwrap()
}
Frontend на Vue 3:
✅ Vue 3 + Vite → Быстрый UI.
✅ Nuxt 3 (SSR + SSG) → Серверный рендеринг.
✅ Vue Apollo (GraphQL) → Запросы к backend.
✅ WebAssembly (Rust + wasm-bindgen) → Ускоренные вычисления.
✅ PWA → Поддержка офлайн-режима.
Установим Vue 3 с Vite:
npm create vite@latest ml-frontend --template vue
cd ml-frontend
npm install
Для серверного рендеринга используем Nuxt 3:
npx nuxi init ml-frontend
cd ml-frontend
npm install
В nuxt.config.ts включим SSR:
export default defineNuxtConfig({
ssr: true,
});
Устанавливаем Apollo Client:
npm install @apollo/client graphql
Создаем composables/useGraphQL.ts:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
const client = new ApolloClient({
uri: "http://localhost:3000/graphql",
cache: new InMemoryCache(),
});
export default function useGraphQL() {
return { client, gql };
}
В pages/index.vue загружаем данные:
<script setup>
import useGraphQL from "@/composables/useGraphQL";
const { client, gql } = useGraphQL();
const { data } = await client.query({
query: gql`
query {
hello
}
`,
});
</script>
<template>
<h1>{{ data?.hello }}</h1>
</template>
Установим wasm-pack:
cargo install wasm-pack
Создадим wasm-модуль:
cargo new --lib wasm_ml
cd wasm_ml
Добавим wasm-bindgen в Cargo.toml:
[dependencies]
wasm-bindgen = "0.2"
В src/lib.rs реализуем ML-функцию:
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add_numbers(a: f64, b: f64) -> f64 {
a + b
}
Компилируем WebAssembly:
wasm-pack build --target web
Добавим в Vue:
npm install wasm-ml
Подключим в pages/index.vue:
<script setup>
import init, { add_numbers } from "wasm-ml";
await init();
const result = add_numbers(10, 20);
</script>
<template>
<h1>10 + 20 = {{ result }}</h1>
</template>
Устанавливаем vite-plugin-pwa:
npm install vite-plugin-pwa
В vite.config.ts:
import { defineConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa";
export default defineConfig({
plugins: [VitePWA({ registerType: "autoUpdate" })],
});
Добавляем manifest.json:
{
"name": "ML App",
"short_name": "ML",
"icons": [{ "src": "/icon.png", "sizes": "512x512", "type": "image/png" }],
"start_url": "/",
"display": "standalone"
}
при объединении всего вышеперечисленного в один код получим:
├── ml_project
│ ├── backend
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ ├── main.rs
│ │ │ ├── graphql.rs
│ │ │ ├── grpc.rs
│ │ │ ├── ml.rs
│ ├── frontend
│ │ ├── package.json
│ │ ├── nuxt.config.ts
│ │ ├── pages
│ │ │ ├── index.vue
│ │ ├── composables
│ │ │ ├── useGraphQL.ts
│ ├── wasm
│ │ ├── Cargo.toml
│ │ ├── src
│ │ │ ├── lib.rs
│ ├── tauri
│ │ ├── src-tauri
│ │ │ ├── tauri.conf.json
// backend/src/main.rs
use axum::{routing::get, Router};
use std::net::SocketAddr;
use tonic::{transport::Server};
mod graphql;
mod grpc;
mod ml;
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "ML Backend is running!" }));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tokio::spawn(async move { grpc::start_grpc().await });
axum::Server::bind(&addr).serve(app.into_make_service()).await.unwrap();
}
// frontend/pages/index.vue
<script setup>
import useGraphQL from '@/composables/useGraphQL';
const { client, gql } = useGraphQL();
const { data } = await client.query({ query: gql`query { hello }` });
</script>
<template>
<h1>{{ data?.hello }}</h1>
</template>
// wasm/src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add_numbers(a: f64, b: f64) -> f64 {
a + b
}
// tauri/src-tauri/main.rs
fn main() {
tauri::Builder::default().run(tauri::generate_context!()).expect("error");
}