Создание 100% статически связанного однофайлового веб-приложения с React и Rust

Ссылка на оригинал - https://anderspitman.net/blog/static-react-rust-webapp/, автор публикации - Anders Pitman

В этом руководстве будут рассмотрены основы создания минимального приложения React, которое можно развернуть в виде статически связанного двоичного файла Rust. Для этого весь ваш код, включая HTML, JavaScript, CSS и Rust, упакован в один файл, который будет работать практически на любой 64-битной системе Linux, независимо от версии ядра или установленных библиотек.

Полный исходный код доступен на GitHub.

Зачем?

  • Упрощенное развертывание: наличие статического двоичного кода означает, что вам просто нужно скопировать файл на сервер и запустить его.
  • Кроссплатформенные собственные приложения с графическим интерфейсом. Одной из самых больших проблем при создании кроссплатформенного приложения с графическим интерфейсом пользователя является работа с библиотекой графического интерфейса, предназначенной для всех интересующих вас платформ. Такой подход позволяет вам использовать браузер пользователя для этой цели. Это несколько похоже на то, что выполняет Electron, но ваш бэкэнд в Rust, а не в JavaScript, и пользователь переходит к приложению из своего браузера. Здесь, безусловно, есть компромиссы, но это может хорошо работать для некоторых приложений. Впервые я познакомился с этим подходом путем синхронизации , которая написана на go, но делает нечто подобное.
  • Потому что я был одержим статической связью столько, сколько себя помню, и я не совсем уверен, почему.

Предпосылки

Инициализируйте каталог проекта

Мы позволим Cargo управлять каталогом проектов для нас. Запустите следующие команды:

cargo new --bin react_rust_webapp
cd react_rust_webapp

Создание приложения React

Сначала установите React, Babel и Webpack:

mkdir ui
cd ui
npm init -y
npm install --save react react-dom
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react webpack webpack-cli

Затем создайте исходные файлы:

mkdir dist
touch dist/index.html
mkdir src
touch src/index.js

Поместите следующий контент в dist/index.html:

<!doctype html>
<html>
  <head>
    <title>Static React and Rust</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/bundle.js"></script>
  </body>
</html>

И установите в src/index.js следующее:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hi there</h1>,
  document.getElementById('root')
);

Нам также понадобится файл .babelrc:

{
  "presets": [
    "react",
    "env",
  ],
}

И webpack.config.js файл:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        }
      }
    ]
  }
};

Теперь вы должны быть в состоянии проверить, работает ли интерфейс. Запускаем:

npx webpack --mode development

Это сгенерирует dist/bundle.js. Если вы запустите веб-сервер в dist каталоге, вы сможете успешно обработать пример содержимого.

Теперь для части Rust.

Настройка бэкэнда Rust

Перейти в react_rust_webappкаталог:

cd ..

Первое, что нам нужно сделать, это установить веб-фреймворк. Я обнаружил, что Руил отлично подходит для этого простого примера. Я тоже очень люблю Rocket.

Добавьте rouille к вашим зависимостям Cargo.toml:

[package]
name = "react_rust_webapp"
version = "0.1.0"

[dependencies]
rouille = "2.1.0"

Теперь измените, src/main.rs, чтобы иметь следующий контент:

#[macro_use]
extern crate rouille;

use rouille::Response;

fn main() {
    let index = include_str!("../ui/dist/index.html");
    let bundle = include_str!("../ui/dist/bundle.js");
    rouille::start_server("0.0.0.0:5000", move |request| {

        let response = router!(request,
            (GET) ["/"] => {
                Response::html(index)
            },
            (GET) ["/bundle.js"] => {
                Response::text(bundle)
            },
            _ => {
                Response::empty_404()
            }
        );

        response
    });
}

Как это работает?

Во время компиляции include_str! читает указанный файл и вставляет содержимое в виде статической строки в скомпилированный двоичный файл. Эта строка затем доступна как обычная переменная.

Код маршрута только устанавливает две конечные точки HTTP, "/" и "/bundle.js". Вместо того, чтобы возвращать файлы из файловой системы, как это обычно делается с веб-приложением, мы возвращаем содержимое статических строк из двоичного файла.

Чтобы узнать больше об использовании Rouille для более сложных вещей, обратитесь к их документам.

Запуск

Хорошо, теперь, если все прошло хорошо, мы сможем запустить его. Убедитесь, что ui/dist/bundle.js он уже создан, как указано выше. Затем запустите:

cargo run

Он должен запустить сервер через порт 5000. Если вы перейдете по адресу http://localhost:5000 в вашем браузере, вы должны увидеть «Hi there».

Статическое связывание

Эта часть может быть пропущена, если вам не нужно 100% статическое связывание. В любом случае Rust статически связывает большинство библиотек по умолчанию, за исключением таких вещей, как libc.

Если вы хотите продолжить, вам сначала нужно установить musl libc в вашей системе и убедиться, что команда musl-gcc находится в вашем PATH.

Затем повторите груз следующим образом:

cargo run --target=x86_64-unknown-linux-musl

Построение производства

Для меньших двоичных файлов соберите bundle.jsследующее (из с ui/):

npx webpack --mode production

И запустить груз следующим образом:

cargo build --release --target=x86_64-unknown-linux-musl

Вы должны получить статически связанный двоичный файл в react_rust_webapp/target/x86_64-unknown-linux-musl/release/

Заключение

Это только основы. С этим можно многое сделать, в том числе:

  • Используется build.rsдля автоматической сборки приложения React при компиляции Rust.
  • Взять номер порта из командной строки
  • Сериализованные (возможно, JSON) запросы и ответы
  • Запустите webpack как команду сценария npm
  • Ориентируйтесь на другие ОС. Я еще не пробовал, но это должно быть в основном переносимо на MacOS и Windows, благодаря великолепной Rust / Cargo и универсальной доступности веб-браузеров.