Хочу рассказать про один из моих любимых 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
если выключили. Теперь по нажатию кнопки должно приветствоваться имя из файла.
Комментарии (30)
MountainGoat Автор
12.10.2024 13:22Про что б ещё написать? Два способа написать приложение Tauri на одном Расте без JS (но с HTML)? Или как в эту каракатицу встроить ещё и Питон?
Mingun
12.10.2024 13:22Да, статься про написание на расте было бы замечательно. Еще может быть интересно преобразование существующего SPA-приложения. У меня даже идея появилась -- собрать https://ide.kaitai.io/devel/ в такое приложение. Но наверное там побольше работы, чем написание простых врапперов.
iskateli
12.10.2024 13:22Что-нибудь табличное можете пожалуйста продемонстрировать? Насколько удобно будет работать с таблицей на голом расте или через js например, да и производительность хорошо видно на больших табличках
CaptGg
12.10.2024 13:22Tauri - это просто удобная обертка над Microsoft Edge WebView2 (если в Windows). Производительность такая же как в самом Edge, так что проверить легко в самом браузере.
А удобство работы с таблицами из Rust зависит от того, как вы реализуете взаимодействие Rust и JavaScript. Tauri дает удобный интерфейс для реализации этого взаимодействия, но функции пишете вы сами. Какие типы данных передаются и как обрабатываются на обоих концах - решаете вы сами в своем коде.
Можете хоть прямо из Rust строить HTML. А можете реализовать в JS функцию рисующую таблицу из массива данных переданного Rust, что более оптимально.
MountainGoat Автор
12.10.2024 13:22Оптимально как раз строить таблицу в Rust. Потому что можно передавать только те поля, которые видны. Но вот красиво прокручиваться с инерционной прокруткой она не будет.
CaptGg
12.10.2024 13:22Одно другому не мешает, опять же это вопрос реализации функций обмена данными между Rust и JavaScript. Если это просто статическая таблица, то отдавать HTML из Rust будет вполне удобно.
Но если это часть интерактивного пользовательского интерфейса или сама таблица интерактивна, то реализовать её проще и быстрее на JavaScript, обмениваясь с Rust только необходимыми данными.
Vedomir
12.10.2024 13:22Больше Rust интересней. Зачем тут еще Python не очень понятно - JS/TS допустим необходимое зло для написания интерфейса на веб и тут уже вырисовывается один фронт и на веб-мобилки-десктоп, а Python язык примерно того же высокого уровня и я не пытаюсь сказать что он плохой, он тоже хороший, но в нем нет необходимости и его в такой проект актуально добавлять примерно на одном уровне с массой других хороших язков (Go?C#?Kotlin?).
MountainGoat Автор
12.10.2024 13:22Зачем тут еще Python
Я уже несколько раз натыкался, когда для какой-то цели на Python есть замечательная библиотека, а на Rust нету/сырая. Если это не центральная вещь для приложения, а опциональная хотелка, то проще вклеить в EXE шник ещё и Питон, чем разрабатывать с нуля.
Или ещё бывает, что у какого-то сервиса под Питон есть официальный API а под Rust - фигуёки. И тут либо реверсить их протокол, заодно не имея возможности спросить на форуме про проблемы; либо же опять таки встраивать Питон.
Благо для юзера снаружи это совершенно незаметно.
Vedomir
12.10.2024 13:22Ок, спасибо за уточнение, видимо мне не хватает кругозора. Тогда тоже интересно, особенно мне, как стороннему наблюдателю, насколько вырастет потребление памяти для получившегося змея-горыныча
ImagineTables
12.10.2024 13:22В памяти занимает на Win 11 200 Мб. Кто-то считает, что это много. Покажите меньше. Только HelloWorld у нас будет такой
Это многовато. В реальном проекте с расходом памяти менее 100 мегабайт (трудно сказать точно, поскольку дело происходило в чужом адресном пространстве — я смотрел на working set / private bytes чужого процесса до и после встраивания) я имел не просто скриптовый язык (диалект экмы + элементы jQuery), но и полноценную разметку со стилизацией и анимациями (в смысле, в проекте активно использовались эффекты и анимации, а не просто поддерживались движком). Бинарники были менее 5 метров, ещё столько же на ресурсы.
Правда, я замерял под Win7, хз, сколько оно скушало бы под 11.
Astroscope
12.10.2024 13:22Оффтопик
В Windows всегда есть движок от Edge, даже если в говносборке вырезан сам Edge.
Почему, зачем, с какой целью неуемные Васяны вырезают Edge? Чтобы что? Чтобы поставить ровно то же самое, только в незначительных мелочах хуже, но от Google? Нет, я ничего не имею против околотехнического творчества Васянов самого по себе, сделать говносборку с нескучными обоями вполне себе хобби (впрочем, Васянов и говносборок много, а БолгенОС - одна, не имеющая аналогов, поэтому достижение, если сравнивать с настоящими мастерами, так себе), но зачем дурь пропагандировать, без сколь-нибудь полного понимания вырезая "лишнее" - не только Edge, но и другие "плохие" компоненты? Вопрос, пожалуй, скорее риторический.
JakErdy
12.10.2024 13:22Имхо. Как и все поделки на расте, эта выглядит так же шапкозакидательно как и всё остальное "написаное на раст btw". На мой взгляд у tauri слишком много минусов чтобы делать на нём сколь нибудь серьёзный проект.
Отсутствие гарантии наличия web view на компе у конечного пользователя
Отсутствие гарантии что через пару лет сам microsoft не решит выпилить webview из дефолтной поставки, или что будет продолжать его обновлять
Зоопарк браузерных движков на разных платформах, с разным подмножеством поддерживаемых фичь
Rust. Я уж лучше буду на плюсах писать чем на этом творении великого сумрачного гения. Сколько там людей на рынке, кто согласиться писать на расте нативные расширения не связанные с криптой, и прочим модным молодёжным?
Короче говоря, если это не пет проект, и жизненный цикл продукта больше чем 5 лет, electron будет сильно лучше и надёжнее. Плюс ко всему старые билды будут запускаться, и что самое главное работать, вне зависимости от того что там снаружи.
McHack
12.10.2024 13:22В конфиге Tauri можно указать, чтобы он чекал наличие WebView. Если его нет, то программа предложит его докачать. Делается одной строчкой в конфиге
Смотрим первый пункт
На то и существуют стандарты ES и сама Javascript Foundation, чтобы регулировать весь этот зоопарк. Уже давно не нулевые, где каждый делал что хотел. Времена jQuery уже давно прошли, добро пожаловать в современный вэб
Дело вкуса, тут не поспоришь
WebView уже на вряд-ли покинет стандартный набор библиотек любой ОС. Это неотъемлемая часть взаимодействия с системой. У Майков даже системные вещи уже через WebView работают, если что на 11 Винде новая нижняя панель на Реакте написана (тут могу ошибаться)
Flux
12.10.2024 13:22для создания десктопно-мобильных приложений на JavaScript
Нет, не попробуем. Дальше можно не читать.
CrashLogger
12.10.2024 13:22На что только люди не идут, лишь бы не учить нормальный язык вместо JS )
Vedomir
12.10.2024 13:22Ну тут как посмотреть. Есть идея некого (пет)проекта с десктопно-мобильным приложением. Понятно что скорее всего вообще не займусь,а если и займусь то сделаю что-то минимальное, но от платформы хочется полноценной поддержки всех основных десктопных и мобильных платформ включая Linux, веб тоже хорошо бы, и как обязательное требование наличие качественного визуального редактора текста. С/С++ как-то совсем не хочется.
Что остается? Flutter, Electron/React Native. Теперь еще Tauri.
Является ли Dart нормальным языком по сравнению с TypeScript?
В свое время много писал на С#, но например MAUI и Blazor в WebView не поддерживают Linux, а в Avalonia нет визуального редактора текста.
Wails не поддерживает мобильные платформы и там тоже фронт на TS.
MountainGoat Автор
12.10.2024 13:22У меня аргумент проще. Сквозь UI на JavaScript прокручивается на 2 порядка больше бабла чем через все остальные UI вместе взятые. Угадайте, где всё максимально отлажено, отполировано и задокументировано?
Flux
12.10.2024 13:22всё максимально отлажено, отполировано и задокументировано
Настолько отполировано что для функции is_number нужна отдельная библиотека?
JRJ2309
12.10.2024 13:22Не особо люблю tauri, да и тыкал его всего раз на релизе, но статья написана оч классно, легко и интересно) спасибо автору, появился повод поиграться с платформой на досуге
Vedomir
12.10.2024 13:22Хорошая статья и есть надежда на появление как минимум еще одной платформы для кросс-плафторменных приложений. Чуть выше написал про проблему с выбором сейчас из имеющихся альтернатив, наверное не буду повторяться.
MountainGoat Автор
Я считаю, что Tauri может заменить не только Electron, но в перспективе и Flutter тоже.
SserjIrk
Если говорить о Flutter то разница с первых строк:
"... . В памяти занимает на Win 11 200 Мб. Кто-то считает, что это много. Покажите меньше. .."
У Flutter вместе с движком демка собирается в 23Мб, в памяти занимает 30Мб.
Старая добрая JavaFX (OpenJFX) собранная через GraalVM в нативный exe получается 25 Мб, в памяти 28.
Добавить видео в обе эти вещи легко довесив 10-15 МБ обвязок над облегченным FFmpeg.
MountainGoat Автор
Не могу проверить. Ни на какой странице с демками Flutter я не нашёл готового EXE, а когда попробовал собрать сам - там что-то править в коде надо и т.д.
Ещё большой вопрос, как мерять. Task Manager и для Tauri показывает 3 мегабайта. Но извините, у меня там один массив больше.
SserjIrk
Если действительно интересно результат двух команд в терминале, без открытия редактора:
flutter create . --org ru.sserjirk --project-name testnative --platforms windows
flutter build windows
Можете взять тут: https://disk.yandex.ru/d/eDCdcl-P4UTIHw
MountainGoat Автор
Спасибо, сравню на днях.
Я выложил демку, которая получается в этой статье. Пароль "habr"
Octabun
На Flutter ничего править в коде не надо, даже не представляю как можно на такое выйти. Но это не важно. Важно что вы сравниваете несравнимое.
Flutter - чисто нативное приложение и рисует своими силами. Tauri чисто Web и рисует силами чужого браузера. Так что сравнивать надо когда во Flutter приложение вставлен Web View, и если при этом Flutter потребит меньше ресурсов, то он лучше. По ресурсам.
Vedomir
С другой стороны они при этом оба решают одну и ту же задачу - вывода пользовательского интерфейса. Для меня главный вопрос с Flutter это Dart - идея учить еще один узкоспециализированный язык который жестко привязан к одной-единственной технологии несколько смущает.