1919* **运行环境**: 浏览器端直接运行 (Browser Standalone),无需 Node.js 构建步骤。
2020* **文件结构**: 单个 HTML 文件,逻辑代码编写在 `<script type="text/eficy">` 标签中。
2121* **导入规范**:
22- * **核心功能**: 统一从 `'eficy'` 导入 (`initEficy`, `render`, `signal`, `computed`, `effect`, `observer`, `peek`, `batch`).
22+ * **核心功能**: 统一从 `'eficy'` 导入 (`initEficy`, `render`, `signal`, `computed`, `effect`, `observer`, `component`, `bind`, ` peek`, `batch`).
2323 * **UI 组件**: 统一从 `'shadcn'` 导入 (`Button`, `Input` 等,但使用时需加 `e-` 前缀).
2424 * **样式**: 推荐使用 Tailwind 语法 (`className="flex p-4"`),Eficy 内置 UnoCSS 引擎会自动处理。
2525
3030| 特性 | Eficy (Signal) | React (Hooks) | 说明 |
3131| :--- | :--- | :--- | :--- |
3232| **定义状态** | `const count = signal(0)` | `const [count, set] = useState(0)` | **优先使用 Signal** |
33- | **读取值** | `count()` | `count` | Signal 需调用函数取值 |
34- | **更新值** | `count(1)` 或 `count.set(1)` | `setCount(1) ` | 推荐使用 `.set(val )` 或函数调用 `count(val)` |
33+ | **读取值** | `count()` 或 `count.get()` 或 `count.value` | `count` | 多种读取方式 |
34+ | **更新值** | `count(1)` 或 `count.set(1)` 或 `count.value = 1 ` | `setCount(1 )` | 多种更新方式 |
3535| **基于旧值更新** | `count(c => c + 1)` | `setCount(c => c + 1)` | 均支持函数式更新 |
3636| **衍生状态** | `const double = computed(() => count() * 2)` | `useMemo(...)` | **必须使用 computed** |
3737| **副作用** | `effect(() => console.log(count()))` | `useEffect(...)` | **必须使用 effect** |
38+ | **双向绑定** | `<input {...bind(name)} />` | 手动绑定 | 使用 `bind()` 简化表单 |
3839
3940### 🚫 常见错误预警
4041* **严禁**混用 `useState` / `useEffect` 来管理业务状态。仅在极少数需要通过 Ref 操作原生 DOM 且与 Signal 无关的场景下才考虑 React Hooks。
@@ -88,18 +89,20 @@ effect(() => {
8889 <span>{countSignal}</span> // 框架会自动订阅 textContent
8990 ```
9091
91- **规则 C: 自定义组件与 `observer`**
92- * 当你编写自定义组件,并且在组件**逻辑体**中读取了 Signal 的值 (例如 `count()`) 用于计算或条件渲染时,**必须**使用 `observer` 包裹该组件。
92+ **规则 C: 自定义组件与 `observer` / `component`**
93+ * 当你编写自定义组件,并且在组件**逻辑体**中读取了 Signal 的值 (例如 `count()`) 用于计算或条件渲染时,**必须**使用 `observer` 或 `component` 包裹该组件。
94+ * `component()` 是 `observer()` 的语法糖,推荐使用:
9395
9496```tsx
95- import { observer } from 'eficy';
97+ import { component, signal } from 'eficy';
9698
97- // ✅ 场景 1: 逻辑中读取了 Signal -> 需要 observer
98- const UserGreeting = observer(({ user }) => {
99- // 在渲染函数中读取了 user().name
100- if (user().isGuest) return <div>Welcome Guest</div>;
101- return <div>Welcome {user().name}</div>;
99+ // ✅ 推荐:使用 component() 包裹
100+ const Counter = component(() => {
101+ const count = signal(0);
102+ return <button onClick={() => count(c => c + 1)}>{count}</button>;
102103});
104+
105+ // 等价于: const Counter = observer(() => { ... });
103106```
104107
105108**规则 D: 复合组件命名**
@@ -108,7 +111,17 @@ const UserGreeting = observer(({ user }) => {
108111
109112### 3.3 表单输入最佳实践 (Input Handling)
110113
111- 1. **文本输入**:
114+ 1. **使用 bind() 简化双向绑定** (推荐):
115+ ```tsx
116+ import { signal, bind } from 'eficy';
117+ const name = signal('');
118+ const checked = signal(false);
119+
120+ <e-Input {...bind(name)} />
121+ <e-Checkbox {...bind(checked)} />
122+ ```
123+
124+ 2. **文本输入** (手动绑定):
112125 * 推荐使用原子 Signal: `const name = signal('')`.
113126 * 绑定: `<e-Input value={name} onChange={e => name(e.target.value)} />`.
114127
@@ -182,7 +195,7 @@ batch(() => {
182195 <script type="module" src="https://unpkg.com/@eficy/browser@1.1.1/dist/standalone.mjs"></script>
183196
184197 <script type="text/eficy">
185- import { initEficy, render, signal, computed, effect, observer, peek, batch } from 'eficy';
198+ import { initEficy, render, signal, computed, effect, observer, component, bind, peek, batch } from 'eficy';
186199 import * as shadcnUi from 'shadcn';
187200
188201 // 1. 初始化
0 commit comments