Ripple 是一个全新的开源前端框架,它借鉴了 React、SolidJS 和 Svelte 的思想,采用 TypeScript 优先、面向组件、类似 JSX 的编译语言,并具有细粒度的响应性和作用域 CSS。由 Svelte 维护者 Dominic Gannaway 创建,Ripple 提供了一个自动依赖跟踪的响应性系统,并且没有虚拟 DOM,直接更新 DOM。Ripple 旨在通过 AI 代理支持更好的调试。
以下代码示例展示了许多核心的Ripple功能:
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,开发者编写组件(例如,TodoList,Counter),这些组件是包含DOM表达式作为语句的函数,其语法主要借鉴了TypeScript和JSX。因此,这些函数描述了应用程序用户界面的一部分的标记(DOM)、样式(CSS)和行为。
用户界面标记直接表明为具有自然控制流(例如 if (todos.length > 0),for (const todo of todos))和 JSX 标记的语句。样式是针对组件的。行为由事件处理程序驱动,利用细粒度的反应性系统,该系统(与 Svelte 类似)在响应独立和计算变量的变化时,像外科手术一样更新真实的 DOM(而不是重新计算组件的整个虚拟 DOM 标记)。
原始值track 描述一个独立变量(例如,count),其值通过@ 操作符获取。计算变量可以表达其对独立变量的依赖(例如,let double = track(() => @count * 2);)。Ripple 的反应式系统确保计算变量与其依赖保持同步。Ripple 的反应式系统还确保 DOM 元素的状态与其依赖保持同步(例如,点击按钮将增加count,这将相应地更新两个段落的textContent 属性)。
Gannaway 在推特上解释:
Ripple 的反应性系统不是虚拟 DOM,也不是信号。它是一个细粒度的、懒惰评估系统,更多地利用编译器而不是运行时来实现这些事情。
尽管Ripple不支持全局状态,但它支持上下文,用于那些必须在组件之间共享的应用状态部分。不过,上下文只能被在闭包中具有它的组件使用,并且只能在组件上下文中设置和读取(即,它们不能在事件处理程序上下文中设置或读取)。
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关键字与状态变化关联:
import { track, effect } from 'ripple';
export component App() {
let count = track(0);
effect(() => {
console.log(@count);
});
<button onClick={() => @count++}>{'Increment'}</button>
}
Ripple 希望为网页应用程序开发人员提供一个更简单的心理模型和更好的开发者体验(例如,不需要useMemo,CSS默认是作用域的,标记和DOM元素之间没有额外的抽象)。该语言旨在与编译器互动,以细致地理解TypeScript类型和响应式状态模式,为更好的自动完成、错误检查和工具支持铺平道路。Ripple团队正在研究将AI支持直接集成到开发服务器中,以进行主动调试和提议。
Ripple 由 Dominic Gannaway 创建,他之前在 Meta 上负责 React Hooks,创建了 Lexical,著有 Inferno,并且最近是 Svelte 5 核心团队的一部分。虽然 Ripple 已经有几年历史,但直到 最近才开源,采用 MIT 许可证。Ripple 仍处于早期开发阶段。欢迎贡献,贡献应 遵循贡献指南。