فریمورک فولاستک Topcoat؛ ریلز Rust از تیم Tokio
حدود ۶ دقیقه مطالعه
فریمورک فولاستک Topcoat برای ساختن برنامهی کامل وب با Rust طراحی شده، از صفحهی HTML تا ارتباط با دیتابیس. سازندهاش تیم Tokio است، همان تیمی که کتابخانهی معروف async برای Rust را نوشته. ایدهی اصلی ساده است: بیشتر کار را روی سرور انجام بده و فقط جاهایی که واقعاً لازم است، JavaScript به مرورگر بفرست.
چرا چنین فریمورکی ساخته شد؟
اگر با Rust برنامهی وب نوشته باشی، احتمالاً Axum را میشناسی. Axum برای ساختن API عالی است، ولی اگر بخواهی برنامهای کامل با صفحه، فرم، session و فایلهای استاتیک بسازی، باید کلی ابزار را خودت کنار هم بگذاری. Topcoat این کار را برایت انجام میدهد. خود تیم Tokio هم میگوید انتظار دارد خیلی از پروژهها Axum و Topcoat را با هم استفاده کنند.
کارل لرش، سازندهی Tokio، دلیل دیگری هم آورده است. به نظر او وقتی با هوش مصنوعی کد مینویسی، فریمورکی که قواعد مشخص و امکانات آماده دارد بهتر جواب میدهد، چون مدل کمتر اشتباه میکند و توکن کمتری مصرف میشود. این نظر خود اوست و مدرکی برایش منتشر نشده.
اولین برنامه
نمونهی hello world از README را ببین. صفحه و کامپوننت هر دو async هستند و HTML داخل ماکروی view نوشته میشود.
use topcoat::{
Result,
router::{module_router, page},
view::{View, component, view},
};
#[tokio::main]
async fn main() {
topcoat::start(module_router!().build()).await.unwrap();
}
#[page]
async fn home() -> Result<impl View> {
Ok(view! {
<!DOCTYPE html>
<html>
<body>
hello(name: "World")
</body>
</html>
})
}
#[component]
async fn hello(name: &str) -> Result<impl View> {
Ok(view! { <h1>"Hello, " (name) "!"</h1> })
}
چون کامپوننت async است، میتواند وسط ساختن صفحه از دیتابیس بخواند یا بررسی کند کاربر اجازهی دیدن صفحه را دارد، و لازم نیست برای این کار یک API جدا بسازی. مسیرها هم از روی ساختار ماژولها تشخیص داده میشوند.
داخل view میتوانی از حلقه و شرط معمولی Rust استفاده کنی. در این مثال، منوی بالای سایت لینک صفحهی فعلی را مشخص میکند:
view! {
<nav>
for item in nav_items {
<a
href=(item.url)
if item.url == current_path {
aria-current="page"
class="active"
}
>
(item.label)
</a>
}
</nav>
}
تعامل در مرورگر، بدون WebAssembly
در Leptos و Dioxus کد Rust به WebAssembly تبدیل میشود و در مرورگر اجرا میشود. Topcoat راه دیگری میرود. هر چیزی که داخل $(...) بنویسی کد Rust است و کامپایلر نوعهایش را چک میکند. این کد یک بار روی سرور اجرا میشود تا صفحهی اول ساخته شود، و همان کد به JavaScript هم ترجمه میشود تا در مرورگر کار کند. پس نه فایل wasm داری و نه مرحلهی build جدا برای بخش کلاینت.
در مثال بعد، یک دکمه پاراگرافی را نشان میدهد یا پنهان میکند، و برای این کار اصلاً درخواستی به سرور نمیرود:
#[component]
async fn faq(cx: &Cx) -> Result<impl View> {
let open = signal(cx, || false);
Ok(view! {
// Runs entirely in the browser; no server round-trip.
<button @click=$(|_e| open.set(!open.get()))>"What is Topcoat?"</button>
<p :hidden=$(!open.get())>"A full-stack Rust framework."</p>
})
}
وقتی به دادهی سرور نیاز داری
بعضی کارها فقط در مرورگر انجام نمیشوند. مثلاً نتیجهی جستوجو باید از دیتابیس بیاید. برای این حالت Topcoat چیزی به اسم shard دارد. کامپوننتی که با ویژگی shard مشخص شود، هر وقت ورودیهایش عوض شوند روی سرور دوباره ساخته میشود و HTML جدیدش جای بخش قبلی صفحه مینشیند. در این مثال با هر حرفی که کاربر تایپ میکند، فقط لیست نتایج عوض میشود:
#[component]
async fn search(cx: &Cx) -> Result<impl View> {
let query = signal(cx, String::new);
Ok(view! {
<input @input=$(|e: Event| query.set(e.target.value))>
// Updates as the user types.
search_results(query: $(query.get()))
})
}
#[shard]
async fn search_results(cx: &Cx, query: String) -> Result<impl View> {
Ok(view! {
<ul>
// Your own server-side code, like a database query:
for product in search_products(cx, &query).await? {
<li>(product.name)</li>
}
</ul>
})
}
از جولای تا حالا این بخشها پیشرفت کردهاند. طبق وبلاگ تیم، نسخهی 0.9 که ۲۴ سپتامبر آمد سه چیز اضافه کرد: فرستادن چند بهروزرسانی پشتسرهم به صفحه، فرستادن تغییر از سرور با WebSocket، و دو کامپوننت آماده به اسم suspense و error_boundary. تغییرهای HTML هم طوری روی صفحه اعمال میشوند که فوکوس و متن واردشده در فیلدها از بین نرود.
ابزارهای دیگر
کنار اینها چیزهایی هست که معمولاً باید خودت پیدا کنی و وصل کنی. کامپوننتهای آمادهی رابط کاربری روی Tailwind ساخته شدهاند و با CLI داخل پروژهی خودت کپی میشوند، پس میتوانی مستقیم تغییرشان بدهی. فایلهای استاتیک با آدرسی سرو میشوند که بخشی از آن هش محتوای فایل است. cookie، session، چندزبانهسازی، ارسال ایمیل و هماهنگی با htmx و Alpine AJAX و Datastar هم در فهرست قابلیتها آمده.
برای دیتابیس باید سراغ Toasty بروی، یک ORM ناهمزمان که همین تیم میسازد.
ایرادها و محدودیتها
اول، پروژه هنوز پخته نیست. فایل README صریح مینویسد که پروژه در مرحلهی اولیه و آزمایشی است و ممکن است تغییرهایی بیاید که کد تو را خراب کند. وبلاگ جولای میگوید Topcoat آمادهی استفاده است، ولی همانجا هم میگوید بخش تعامل سمت مرورگر هنوز محدودیت دارد. در پست سپتامبر هم نویسندهها میگویند هنوز دارند شکل نهایی فریمورک را پیدا میکنند.
دوم، فقط بخشی از زبان Rust داخل $(...) قابل ترجمه به JavaScript است. هر کدی را نمیتوانی آنجا بنویسی.
سوم، خود لرش گفته Topcoat برای برنامههای خیلی بزرگ و پیچیده، مثل Figma، ساخته نشده و برای برنامههای معمولی که با دیتابیس کار میکنند مناسبتر است. اگر رابط کاربریات تقریباً همهچیز را در مرورگر انجام میدهد، Leptos یا Dioxus انتخاب بهتری هستند.
چهارم، چند قابلیت مهم هنوز نیست. در نقشهی راه README احراز هویت، کارهای پسزمینه و islands جزو کارهای آینده آمدهاند. اگر پروژهات به اینها نیاز دارد، فعلاً باید خودت پیادهشان کنی.
نتیجه
اگر Rust بلدی و میخواهی یک ابزار داخلی یا یک برنامهی کوچک با دیتابیس بسازی و همهچیز در یک زبان باشد، Topcoat ارزش امتحان کردن دارد. برای محصولی که باید سالها کار کند، فعلاً احتیاط کن: نسخه را ثابت نگه دار و آماده باش که API عوض شود. ستارههای گیتهاب (حدود ۶٫۲ هزار در زمان نوشتن) فقط نشان میدهد مردم به پروژه علاقه دارند، نه اینکه پایدار است.
منابع:
