Найти тему
Один Rust не п...Rust

Rust + Vue3 = mobile app

Оглавление

Для чего нужна данная статья? :


- Получить представление о написание мобильного приложения с 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.


GitHub - nicktretyakov/gRUSTpcWEB

Настройте среду разработки: 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");

}