Хочу рассказать про один из моих любимых Opensource проектов: Tauri. Это среда для создания десктопно-мобильных приложений на JavaScript, но быстрых и легковесных. С опциональными дополнениями на Rust, а через него и на всех других языках.

Достоинства.

Поддерживается Линь, Вынь и Дрюнь и всякое Ябло. EXEшник HelloWorld весит 1 мегабайт ( ну 5, если не ужиматься в угол как сирота в барском доме ). В памяти занимает на Win 11 200 Мб. (Из них >90% приходится на системные компоненты, расшаренные с другими программами. Поэтому простые средства измерения покажут 5 Мб). Кто-то считает, что это много. Покажите меньше. Только HelloWorld у нас будет такой: в верхней половине окна играет видео с вэба со звуком, а под ним кнопка закрыть, которая при наведении мыши начинает вращаться, и оба эти компонента рендерятся на GPU.

Это достигается тем, что Tauri не тащит с собой браузер, а использует тот, что уже есть в системе. И в самом деле, зачем? В Windows всегда есть движок от Edge, даже если в говносборке вырезан сам Edge. В Андроиде и Ябле всегда есть есть WebKit. Ну а линуксоидов зависимостями не напугаешь, там требуется пакет GTK-WebView. Поддерживается даже Win7/8, но там нужен пакет совместимости весом ~200Мб. Таким образом получается, что нам встретится всего два движка - Хром и WebKit, и на дворе 2024 год, поэтому разница между ними не такая уж значительная.

Вторая приятная сторона Tauri - это гибкость. Полнофункциональное GUI приложение на нём можно написать как целиком на Rust, без строчки на JS, так и целиком на JS, c ровно одной строчкой на Rust. Поэтому знать Rust для его использования не обязательно совсем. Tauri предоставляет в пространство JS модули API для операций, недоступных из браузера - вроде прямого доступа к файлам и железу.

Зрада! Возможность работать только на JS появилась в Tauri 2, который вышел около года назад. Поэтому в сети до сих пор много ссылок на документацию первой версии, где написано, что это всё невозможно.

По сравнению с в чём-то похожим PyWebView, Tauri не имеет папки с файлами, которую нужно носить с собой или распаковывать при каждом запуске, не злит антивирусы распаковками и не требует для работы открытый порт.

Ну и новичков порадует генератор новых проектов с поддержкой TypeScript, React, Vue, чёрта с рогами и без. Потому что под капотом там сидит Vite. Во время разработки есть Hot Reload.

Начинаем

Так, хватит мне тут изображать торговца орифлеймом, давайте лучше кодить. Сейчас я подразумеваю, что читатель - чукча фронтеэндщик, Rust не знает и не хочет, а сидит под Windows. И использует PowerShell. Для остальных вот официальные инструкции.

Ставим Rust. Идём на его сайт и качаем утилиту rustup (64 бит) , которая по сути есть инсталлятор Rust. Ставим всё по умолчанию. Под Windows Rust использует компилятор Microsoft, а тот в свою очередь идёт с большой пачкой библиотек, поэтому установка займёт долго и качает много.

Теперь поставим NodeJS ради его утилиты npm. Открываем терминал и набираем winget install OpenJS.NodeJS.LTS Сработает - хорошо. Если winget опять отвалился, качаем NodeJS с его сайта.

Текстовый редактор оставлю на ваше усмотрение. Для VSCode есть экстеншн для Rust (rust-analyzer) и отдельный специально для Tauri, и ещё пригодится Better TOML.

Проверяем. Вводим в консоли cargo -V (большая) потом npm -v (маленькая). Если выводит номера версий - то всё в порядке.

Сам Tauri ставить не надо, он качается в проект.

Создаём проект.

Зрада! Даже если вы собираетесь писать на JavaScript, при создании проекта надо выбирать TypeScript. Это потому, что для проекта TypeScript ставится Vite и прочие ништяки, а проект на JavaScript суперминималистичный. Чтобы в TypeScript-проекте начать писать на JavaScript - достаточно переименовать файл из .ts в .js и убрать его из tsconfig.json.

Открываем в PowerShell папку, где у нас лежат поделки, и набираем npm create tauri-app@latest Нам зададут несколько вопросов, везде нужно выбирать "TypeScript" "Vanilla" а остальное по умолчанию. Когда создание закончится, нужно перейти в папку проекта, набрать один раз npm install а потом npm run tauri dev Наш тестовый проект соберётся и запустится! Теперь, не выключая его, откроем в проекте файл index.html, поменяем в нём что нибудь (заголовок в h1 например) и сохраним. Сразу увидим изменения в запущенном приложении. Кстати, по правому клику в приложении доступно меню с отладочными инструментами. Оно есть только в debug сборке.

Давайте пройдёмся по созданному проекту. В корне у нас лежит index.html. Это HTML главного окна приложения. Честно говоря, не знаю, зачем он вынесен сюда, поскольку вся остальная web-часть убрана в папку src. Ещё мы видим надеюсь знакомые вам package.json и папку node_modules - следы жизни npm. В папку dist кладутся промежуточные стадии упаковки, эту папку не нужно бэкапить или класть в git. Но самая мякотка лежит в папке src-tauri.

В ней: cargo.toml - основной файл нашего проекта. В нём надо зайти и прописать название приложения, версию и автора - эти данные пойдут в метадату ЕХЕ-шника. Заодно в разделе dependencies убедитесь, что tauri у вас второй версии, а то были странные прецеденты.
cargo.lock руками трогать не надо, там список фактически используемых пакетов Rust.

Папка target - туда идут результаты компиляции. Это та папка, которую не надо бэкапить или класть в git. Если в корне проекта выполнить команду npm run tauri build то в папке target/release появится финальная версия нашей программы, со всеми ресурсами упакованными внутрь. Мало того, в папке release/bundle будет программа, запакованная в .msi. Причём это умный инсталлятор, который проверит и предложит скачать все зависимости.

Папка icons содержит иконку нашей программы в вариантах под все платформы. К счастью, это безобразие нам не нужно делать руками. Нам нужна одна иконка в формате PNG размером 1024 пикселя с прозрачностями в интересных местах. Затем в корне проекта вызываем npm run tauri icon /path/to/app-icon.png и все виды иконок появятся сами.

В src-tauri/src лежат непосредственно исходники на Расте. Их сегодня трогать почти не будем. Но можно заглянуть и поразиться минимальности. Изменения исходников на Расте не подхватываются на лету, приложение надо перезапускать.

Настройка Tauri

И наконец файл tauri.conf.json, и capabilities - часть его. Это очень важный конфиг самого Tauri. Открываем.

productname, version, identifier используются при сборке под мобильники. identifier должен быть уникальным в мире, поэтому проявите фантазию.

app - withGlobalTauri это костыль, включённый по умолчанию. Давайте напишем туда false и будет сразу делать как надо (ниже покажу).

window - это параметры главного окна приложения. Их вообще-то много. А можно наоборот, удалить окно отсюда и создавать из кода, но это в другой раз.

Остальное сейчас не важно.

Давайте покодим!

Не буду учить кодить на JavaScript. Вместо этого давайте покажу, как обойтись без бэкэнда на расте, с одним JS. Для этого нам понадобятся так называемые "плагины", они дают нам доступ к системе.

Открывам src/main.ts. Один плагин тут уже есть: api/core. Он позволяет передавать данные на лету между бэком на расте и фронтом. Можно, конечно, было бы и сокет открыть для этого дела и любой другой способ, но через плагин намного проще и быстрее. Посмотрим, что происходит. Из плагина импортируем функцию invoke и вызываем её с аргументом "greet". Что это за greet? Это функция, объявленная в файле src-tauri/lib.rs, а макрос #[tauri::command] делает её доступной из JS.

Зрада! Если вы всё таки кодите на Расте, то помните, что при работе с Tauri нужно по максимуму пользоваться npm, а cargo вызывать только для самых Растовых операций вроде cargo add. Иначе временами вылезают эфемерные баги с неопределённым положением в пространстве. В документации есть целый раздел об этом.

Давайте вместо функции на Расте, прочитаем текст из файла. Создадим текстовый файл, у меня это будет E:/name.txt. За работу с файлами у нас отвечает плагин fs.

В отличие от плагина core, который есть всегда, большинство плагинов нужно установить и потом ещё включить. Сначала в корне проекта набираем npm run tauri add fs а потом в capabilities/default.json делаем

"permissions": [
    "core:default",
    "shell:allow-open",
    "fs:default",
    {
      "identifier": "fs:allow-open",
      "allow": [{ "path": "**" }]
    },
    {
      "identifier": "fs:allow-read",
      "allow": [{ "path": "**" }]
    }      
  ]

Мы разрешили себе чтение из любого файла в системе. Лучше, конечно, по возможности ограничивать пути. Всё, теперь в main.ts можно изменить функцию greet на чтение из файла.

import { open } from "@tauri-apps/plugin-fs";
async function greet() {
  if (greetMsgEl && greetInputEl) {
    // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
    const file = await open("E:/name.txt" );
    const buf = new Uint8Array(20);
    const numberOfBytesRead = await file.read(buf); // 11 bytes
    const text = new TextDecoder().decode(buf);  // "hello world"
    await file.close();
    greetMsgEl.textContent = "Да здравствует товарищ" + text;
  }
}

Ну и всё. Запускаем опять npm run tauri dev если выключили. Теперь по нажатию кнопки должно приветствоваться имя из файла.

Комментарии (10)


  1. MountainGoat Автор
    12.10.2024 13:22

    Я считаю, что Tauri может заменить не только Electron, но в перспективе и Flutter тоже.


    1. SserjIrk
      12.10.2024 13:22

      Если говорить о Flutter то разница с первых строк:

      "... . В памяти занимает на Win 11 200 Мб. Кто-то считает, что это много. Покажите меньше. .."

      У Flutter вместе с движком демка собирается в 23Мб, в памяти занимает 30Мб.

      Старая добрая JavaFX (OpenJFX) собранная через GraalVM в нативный exe получается 25 Мб, в памяти 28.

      Добавить видео в обе эти вещи легко довесив 10-15 МБ обвязок над облегченным FFmpeg.


      1. MountainGoat Автор
        12.10.2024 13:22

        Не могу проверить. Ни на какой странице с демками Flutter я не нашёл готового EXE, а когда попробовал собрать сам - там что-то править в коде надо и т.д.

        Ещё большой вопрос, как мерять. Task Manager и для Tauri показывает 3 мегабайта. Но извините, у меня там один массив больше.


        1. SserjIrk
          12.10.2024 13:22

          Если действительно интересно результат двух команд в терминале, без открытия редактора:

          flutter create . --org ru.sserjirk --project-name testnative --platforms windows

          flutter build windows

          Можете взять тут: https://disk.yandex.ru/d/eDCdcl-P4UTIHw


          1. MountainGoat Автор
            12.10.2024 13:22

            Спасибо, сравню на днях.

            Я выложил демку, которая получается в этой статье. Пароль "habr"


        1. Octabun
          12.10.2024 13:22

          На Flutter ничего править в коде не надо, даже не представляю как можно на такое выйти. Но это не важно. Важно что вы сравниваете несравнимое.

          Flutter - чисто нативное приложение и рисует своими силами. Tauri чисто Web и рисует силами чужого браузера. Так что сравнивать надо когда во Flutter приложение вставлен Web View, и если при этом Flutter потребит меньше ресурсов, то он лучше. По ресурсам.


  1. MountainGoat Автор
    12.10.2024 13:22

    Про что б ещё написать? Два способа написать приложение Tauri на одном Расте без JS (но с HTML)? Или как в эту каракатицу встроить ещё и Питон?


    1. Mingun
      12.10.2024 13:22

      Да, статься про написание на расте было бы замечательно. Еще может быть интересно преобразование существующего SPA-приложения. У меня даже идея появилась -- собрать https://ide.kaitai.io/devel/ в такое приложение. Но наверное там побольше работы, чем написание простых врапперов.


    1. iskateli
      12.10.2024 13:22

      Что-нибудь табличное можете пожалуйста продемонстрировать? Насколько удобно будет работать с таблицей на голом расте или через js например, да и производительность хорошо видно на больших табличках


  1. ImagineTables
    12.10.2024 13:22

    В памяти занимает на Win 11 200 Мб. Кто-то считает, что это много. Покажите меньше. Только HelloWorld у нас будет такой

    Это многовато. В реальном проекте с расходом памяти менее 100 мегабайт (трудно сказать точно, поскольку дело происходило в чужом адресном пространстве — я смотрел на working set / private bytes чужого процесса до и после встраивания) я имел не просто скриптовый язык (диалект экмы + элементы jQuery), но и полноценную разметку со стилизацией и анимациями (в смысле, в проекте активно использовались эффекты и анимации, а не просто поддерживались движком). Бинарники были менее 5 метров, ещё столько же на ресурсы.

    Правда, я замерял под Win7, хз, сколько оно скушало бы под 11.