description: 如何安装和使用库
入门指南
PreffX 是一个自信的 JS 库,用于创建响应式 DOM。它受到 React 和 Preact 的启发,但提供了自己的基于信号的方法。
⚠️ 该项目目前处于实验阶段,请勿在生产环境中使用 ⚠️
基本原则
- 类似 React 的 JSX 语法;
- Preact signals 作为响应式核心;
- 只有信号会触发重新渲染;
- 每个组件只执行一次;
- 组件 props 作为第一个参数传入,所有工具作为第二个参数传入 - 无需导入它们;
- 支持同步和异步函数组件;
- 区分属性和特性(所有属性都以
$为前缀)- 例如,$value是一个属性,而value是一个特性。
安装
尝试 Vite + PreffX 演示
示例
- 简单计数器:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { signal }) => {
const count = signal(0);
return <button
onClick={() => {
count.value += 1
}}
>
Count is {count}
</button>
};- 如何创建元素引用:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { signal }) => {
const signalRef = signal();
return <div $ref={signalRef}>
<div
$ref={(refVal) => {
// 在元素挂载后调用,refVal = HTMLDivElement
// 在元素销毁前调用,refVal = null
}}
>
Refs
</button>
</div>
};- 生命周期钩子:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { onMount, onDestroy }) => {
const { count } = props;
onMount(() => {
// 挂载逻辑
});
onDestroy(() => {
// 销毁逻辑
});
return <button
onClick={() => {
count.value += 1
}}
>
Count is {count}
</button>
};- 列表渲染:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { For }) => {
const items = signal([
{
name: 'First'
},
{
name: 'Second'
}
]);
return <ul>
<For
items={items}
callback={(item) => <li>Item with name: {item.name}</li>}
fallback={<li>No items</li>}
/>
</ul>;
};- 上下文处理:
tsx
import type { PC } from 'preffx';
const contextKey = 'ctx-counter';
const AnotherComponent: PC = (props, { context }) => {
// 读取上下文
const counter = context[contextKey];
return <span>
{counter}
</span>;
};
export const App: PC = (props, { signal, context }) => {
const counter = signal(0);
// 修改上下文
context[contextKey] = counter;
return <p>
{valueFromContext}
<AnotherComponent />
</p>;
};- 异步组件:
tsx
import type { APC } from 'preffx';
import { getData } from './data';
import { AnotherComponent, AnotherAsyncComponent } from './components';
const AsyncComponent: APC<{
name: string;
}> = async (props, utils) => {
const data = await getData()
const componentRoot = await <AnotherAsyncComponent name='nested'/>;
return <div>
<AnotherComponent data={data} />
{componentRoot}
</div>;
}- 错误处理:
tsx
import type { PC } from 'preffx';
import { AnotherComponent } from './components';
export const App: PC = (props, { Catch }) => {
return <div>
有时组件会返回错误
<Catch fallback={<div>Catched!</div>}>
<AnotherComponent />
</Catch>
</div>;
};- 延迟值处理:
tsx
import type { PC } from 'preffx';
import { AsyncComponent } from './components';
export const App: PC = (props, { computed, Defer }) => {
const def = computed(() => <AsyncComponent id={props.id} />);
return <Defer
value={def}
initial={<div>Please wait</div>}
/>;
};- 传送门:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { Portal }) => {
return <div>
<span>当前树中的某些文本</span>
<Portal root={document.getElementById('portal')} >
<div>传送门中的文本</div>
</Portal>
</div>;
};- 唯一标识符:
tsx
import type { PC } from 'preffx';
export const App: PC = (props, { id }) => {
// 获取唯一 id
const inputId = id();
return <div>
<label>
密码:
<input
type="password"
aria-describedby={inputId}
/>
</label>
<p id={inputId}>
密码应至少包含 18 个字符
</p>
</div>;
};