文档
Solid.js 响应式图书收藏
目标
用 Solid.js 实现图书收藏列表,展示 createSignal 响应式状态、createEffect 副作用和 For 控制流。
完整代码
src/index.jsx
import { render } from 'solid-js/web';
import App from './App';
render(() => <App />, document.getElementById('root'));
src/App.jsx
import { createSignal, createEffect, For, Show } from 'solid-js';
import styles from './App.module.css';
const INITIAL_BOOKS = [
{ id: 1, title: '深入理解计算机系统', author: 'Randal E. Bryant', year: 2016, read: true },
{ id: 2, title: '代码整洁之道', author: 'Robert C. Martin', year: 2010, read: false },
{ id: 3, title: '设计模式', author: 'GoF', year: 1994, read: true },
];
export default function App() {
const [books, setBooks] = createSignal(INITIAL_BOOKS);
const [newBook, setNewBook] = createSignal({ title: '', author: '', year: '' });
const [search, setSearch] = createSignal('');
// 派生状态:搜索结果
const filteredBooks = () => {
const term = search().toLowerCase();
if (!term) return books();
return books().filter(b =>
b.title.toLowerCase().includes(term) ||
b.author.toLowerCase().includes(term)
);
};
// 副作用:统计已读数量
createEffect(() => {
const readCount = books().filter(b => b.read).length;
console.log(`已读: ${readCount}/${books().length}`);
});
const addBook = (e) => {
e.preventDefault();
const b = newBook();
if (!b.title || !b.author) return;
setBooks(prev => [...prev, { ...b, id: Date.now(), read: false, year: Number(b.year) || 2024 }]);
setNewBook({ title: '', author: '', year: '' });
};
const toggleRead = (id) => {
setBooks(prev => prev.map(b => b.id === id ? { ...b, read: !b.read } : b));
};
const removeBook = (id) => {
setBooks(prev => prev.filter(b => b.id !== id));
};
return (
<div class={styles.app}>
<h1>📚 图书收藏</h1>
<input
type="text"
placeholder="搜索书名或作者..."
value={search()}
onInput={(e) => setSearch(e.target.value)}
class={styles.search}
/>
{/* 添加表单 */}
<form onSubmit={addBook} class={styles.form}>
<input
placeholder="书名" value={newBook().title}
onInput={(e) => setNewBook(p => ({ ...p, title: e.target.value }))}
/>
<input
placeholder="作者" value={newBook().author}
onInput={(e) => setNewBook(p => ({ ...p, author: e.target.value }))}
/>
<input
placeholder="年份" type="number" value={newBook().year}
onInput={(e) => setNewBook(p => ({ ...p, year: e.target.value }))}
/>
<button type="submit">➕ 添加</button>
</form>
{/* 图书列表 */}
<div class={styles.list}>
<Show when={filteredBooks().length > 0} fallback={<p style="color:#999">没有匹配的图书</p>}>
<For each={filteredBooks()}>
{(book) => (
<div class={`${styles.card} ${book.read ? styles.read : ''}`}>
<div class={styles.info}>
<strong>{book.title}</strong>
<small>{book.author} · {book.year}</small>
</div>
<div class={styles.actions}>
<button onClick={() => toggleRead(book.id)}>
{book.read ? '✅ 已读' : '📖 标记已读'}
</button>
<button onClick={() => removeBook(book.id)} class={styles.del}>🗑️</button>
</div>
</div>
)}
</For>
</Show>
</div>
<p class={styles.stats}>
共 {books().length} 本 · 已读 {books().filter(b => b.read).length} 本
</p>
</div>
);
}
运行步骤
npx degit solidjs/templates/js solid-books
cd solid-books
npm install
# 替换 src/App.jsx
npm run dev
预期输出
- 展示 3 本预置图书,2 本标记已读(绿色)
- 搜索框实时过滤图书
- 添加新书后列表自动响应式更新
- 控制台输出已读统计