فریمورک Ripple، رویکردی تازه به توسعه فرانتاند

Ripple یک فریمورک متنباز و جدید در حوزه فرانتاند است که ایدههایی از React، SolidJS و Svelte را با هم ترکیب میکند و آنها را در قالب زبانی کامپایلشونده، مبتنی بر TypeScript، کامپوننتمحور و شبیه JSX ارائه میدهد. این فریمورک از ریاکتیویتی دانهریز (Fine-grained Reactivity)، CSS اسکوپشده و بهروزرسانی مستقیم DOM بدون استفاده از Virtual DOM بهره میبرد.
Ripple توسط دومینیک گنوی (Dominic Gannaway)، از اعضای کلیدی تیم Svelte، توسعه داده شده و یکی از اهداف اصلی آن، فراهمکردن بستر مناسب برای دیباگ بهتر با کمک ایجنتهای هوش مصنوعی است.
import { Button } from './Button.ripple';
import { track } from 'ripple';
export component TodoList({ todos, addTodo }: Props) {
<div class="container">
<h2>{'Todo List'}</h2>
<ul>
for (const todo of todos) {
<li>{todo.text}</li>
}
</ul>
if (todos.length > 0) {
<p>{todos.length}{" items"}</p>
}
<Button onClick={addTodo} label={"Add Todo"} />
</div>
<style>
.container {
text-align: center;
font-family: "Arial", sans-serif;
}
</style>
}
export component Counter() {
let count = track(0);
let double = track(() => @count * 2);
<div class='counter'>
<h2>{'Counter'}</h2>
<p>{"Count: "}{@count}</p>
<p>{"Double: "}{@double}</p>
<Button onClick={() => @count++} label={'Increment'} />
<Button onClick={() => @count = 0} label={'Reset'} />
</div>
}
معماری کامپوننتها در Ripple
در Ripple، توسعهدهندگان کامپوننتهایی مانند TodoList یا Counter را بهصورت توابعی مینویسند که شامل دستورات مربوط به DOM هستند. این توابع، بهطور همزمان:
- ساختار رابط کاربری (Markup / DOM)
- استایلها (CSS)
- و منطق رفتاری (Behavior)
را برای بخشی از رابط کاربری اپلیکیشن توصیف میکنند.
Syntax این زبان تا حد زیادی از TypeScript و JSX الهام گرفته شده است، اما بهشکل عمیقتری با کامپایلر در تعامل است.
ترکیب طبیعی منطق و UI
در Ripple، مارکآپ رابط کاربری مستقیماً بهصورت دستورات نوشته میشود و امکان ترکیب طبیعی جریان کنترل با UI وجود دارد؛ برای مثال:
- شرطها (
if (todos.length > 0)) - حلقهها (
for (const todo of todos))
در کنار JSX و بدون نیاز به لایههای انتزاعی اضافه استفاده میشوند.
استایلها بهصورت پیشفرض محدود به همان کامپوننت (Scoped) هستند و رفتار کامپوننتها از طریق هندلرهای رویداد و یک سیستم ریاکتیویتی دانهریز کنترل میشود؛ سیستمی که مشابه Svelte، بهجای رندر مجدد کل کامپوننت یا Virtual DOM، فقط همان بخشهای لازم از DOM واقعی را بهروزرسانی میکند.
سیستم ریاکتیویتی Ripple
در Ripple، مفهومی به نام track برای تعریف متغیرهای مستقل (State) وجود دارد. مقدار این متغیرها با عملگر @ خوانده میشود. برای مثال:
countیک متغیر مستقل است- یک متغیر محاسباتی (Computed) میتواند بهصورت زیر تعریف شود:
let double = track(() => @count * 2);
سیستم ریاکتیویتی Ripple بهصورت خودکار وابستگیها را تشخیص میدهد و تضمین میکند که:
- متغیرهای محاسباتی همیشه با وابستگیهایشان همگام باشند
- وضعیت عناصر DOM نیز با تغییر State بهروز شود
برای نمونه، با کلیک روی یک دکمه، مقدار count افزایش پیدا میکند و این تغییر بلافاصله در textContent هر دو پاراگراف وابسته منعکس میشود.
نه Virtual DOM، نه Signals
دومینیک گنوی در توضیح سیستم ریاکتیویتی Ripple در توییتر نوشته است:
«سیستم ریاکتیویتی Ripple نه Virtual DOM است و نه Signals. این یک سیستم ارزیابی تنبل (Lazy Evaluation) و دانهریز است که بیشتر از Runtime، به کامپایلر تکیه میکند تا چنین قابلیتهایی را ممکن کند.»
مدیریت State و Context
Ripple از Global State پشتیبانی نمیکند، اما برای بخشهایی از وضعیت اپلیکیشن که باید بین کامپوننتها به اشتراک گذاشته شوند، مفهوم Context را ارائه میدهد.
البته Context در Ripple محدودیتهایی دارد:
- فقط کامپوننتهایی که Context در Closure آنها قرار دارد میتوانند از آن استفاده کنند
- Context تنها در خود کامپوننت قابل خواندن و تنظیم است
- استفاده از Context داخل Event Handlerها مجاز نیست
import { Context } from 'ripple';
const MyContext = new Context(null);
component Child() {
// Context is read in the Child component
const value = MyContext.get();
// value is "Hello from context!"
console.log(value);
}
export component Parent() {
const value = MyContext.get();
// Context is read in the Parent component, but hasn't yet
// been set, so we fallback to the initial context value.
// So the value is `null`
console.log(value);
// Context is set in the Parent component
MyContext.set("Hello from context!");
<Child />
}
همچنین میتوان با استفاده از کلمه کلیدی effect، افکتها را به تغییرات حالت (state changes) مرتبط کرد:
import { track, effect } from 'ripple';
export component App() {
let count = track(0);
effect(() => {
console.log(@count);
});
<button onClick={() => @count++}>{'Increment'}</button>
}
تجربه توسعهدهنده و آینده Ripple
هدف Ripple ارائه یک مدل ذهنی سادهتر و در عین حال تجربه توسعهدهنده بهتر است. از جمله مزایای آن میتوان به موارد زیر اشاره کرد:
- عدم نیاز به
useMemoو الگوهای پیچیده بهینهسازی - اسکوپ بودن CSS بهصورت پیشفرض
- نبود لایههای انتزاعی اضافی بین مارکآپ و DOM واقعی
زبان Ripple بهگونهای طراحی شده که کامپایلر بتواند درک دقیقی از TypeScript و الگوهای State ریاکتیو داشته باشد؛ موضوعی که مسیر را برای تکمیل خودکار بهتر، خطایابی دقیقتر و ابزارهای توسعه پیشرفتهتر هموار میکند.
تیم Ripple همچنین در حال بررسی ادغام مستقیم قابلیتهای هوش مصنوعی در Dev Server است تا دیباگ پیشدستانه و پیشنهادهای هوشمند در اختیار توسعهدهندگان قرار گیرد.
درباره خالق Ripple
Ripple توسط Dominic Gannaway ساخته شده است؛ توسعهدهندهای با سابقهای قابلتوجه در اکوسیستم فرانتاند. او پیشتر:
- روی React Hooks در Meta کار کرده
- خالق Lexical بوده
- فریمورک Inferno را نوشته
- و اخیراً عضو تیم هستهای Svelte 5 بوده است
اگرچه Ripple چند سالی قدمت دارد، اما بهتازگی با لایسنس MIT بهصورت متنباز منتشر شده و همچنان در مراحل ابتدایی توسعه قرار دارد. مشارکت توسعهدهندگان در این پروژه آزاد است و باید مطابق با راهنمای مشارکت (Contribution Guidelines) انجام شود.




