1.写在前面
本方案特别适合希望在历史遗留的原生JavaScript项目中实现简单轻量级数据驱动机制的开发者。无需引入任何框架或第三方库,即可按照此方法封装出类似于React中useState
的功能,轻松为项目添加状态管理能力,既保持了项目的轻量性,又提升了开发效率
2.优势总结 ★ 了解
- 轻量级响应式系统
-
无虚拟DOM:直接监听状态变化并更新真实DOM,避免虚拟DOM的diff计算开销
-
精准更新:只有订阅了状态变化的DOM元素会更新(相比React的组件级重渲染更精确)
- 类React开发体验
-
提供
useState
+setState
的API设计,降低学习成本 -
支持函数式更新:
setState(prev => prev + 1)
- 状态不可变性
-
自动深拷贝状态,避免意外修改
-
每次更新都生成新状态,便于实现时间旅行调试
- 批量更新优化
-
batch()
可合并多次更新为单次渲染 -
避免频繁DOM操作导致的布局抖动
- 多实例隔离
- 不同模块可以使用独立的状态实例,避免全局污染
3.单例模式 ★ 重要
单例模式效果展示
单例模式封装
javascript
/**
* 单例模式状态管理
* 整个应用共享同一个状态实例
*/
// ==================== 深拷贝工具 ====================
function deepClone(obj, hash = new WeakMap()) {
if (obj == null) return obj;
if (typeof obj !== 'object') return obj;
const constructor = obj.constructor;
const specialTypes = ['Date', 'RegExp', 'Map', 'Set'];
if (specialTypes.includes(constructor.name)) {
return new constructor(obj);
}
if (hash.has(obj)) return hash.get(obj);
const clone = Array.isArray(obj)
? []
: Object.create(Object.getPrototypeOf(obj));
hash.set(obj, clone);
[...Object.getOwnPropertySymbols(obj), ...Object.keys(obj)].forEach(key => {
clone[key] = deepClone(obj[key], hash);
});
return clone;
}
// ==================== 核心实现 ====================
const subscribers = new Map();
let batchQueue = [];
let isBatching = false;
function batchNotify(proxy) {
const callbacks = subscribers.get(proxy);
if (!callbacks) return;
Promise.resolve().then(() => {
callbacks.forEach(cb => {
try {
cb(proxy.value);
} catch (e) {
console.error('回调执行失败:', e);
}
});
});
}
export const useState = (initialState) => {
if (typeof initialState === 'undefined') {
throw new Error('初始状态不能为undefined');
}
const proxy = new Proxy({ value: deepClone(initialState) }, {
set(target, key, value) {
if (key !== 'value') return false;
target[key] = deepClone(value);
if (!isBatching) batchNotify(proxy);
return true;
}
});
return {
get state() { return proxy.value; },
setState: (updater) => {
if (isBatching) {
batchQueue.push({ proxy, updater });
} else {
proxy.value = typeof updater === 'function'
? updater(proxy.value)
: updater;
}
},
subscribe: (callback) => {
if (typeof callback !== 'function') {
throw new Error('回调必须是函数');
}
if (!subscribers.has(proxy)) {
subscribers.set(proxy, new Set());
}
subscribers.get(proxy).add(callback);
return () => {
subscribers.get(proxy)?.delete(callback);
};
}
};
};
export const batch = (callback) => {
if (isBatching) return callback();
isBatching = true;
batchQueue = [];
try {
callback();
const updatesByProxy = new Map();
batchQueue.forEach(({ proxy, updater }) => {
if (!updatesByProxy.has(proxy)) {
updatesByProxy.set(proxy, []);
}
updatesByProxy.get(proxy).push(updater);
});
updatesByProxy.forEach((updaters, proxy) => {
let state = proxy.value;
updaters.forEach(updater => {
state = typeof updater === 'function'
? updater(state)
: updater;
state = deepClone(state);
});
proxy.value = state;
batchNotify(proxy);
});
} finally {
isBatching = false;
batchQueue = [];
}
};
单例模式HTML测试
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>单例模式测试</title>
<script type="module">
import { useState, batch } from './singleton-state.js';
const counter = useState(0);
counter.subscribe(count => {
document.getElementById('count').textContent = count;
console.log('当前计数:', count);
});
// 普通更新
document.getElementById('increment').addEventListener('click', () => {
counter.setState(c => c + 1);
});
// 批量更新
document.getElementById('increment-5').addEventListener('click', () => {
batch(() => {
counter.setState(c => c + 1);
counter.setState(c => c + 1);
counter.setState(c => c + 1);
counter.setState(c => c + 1);
counter.setState(c => c + 1);
});
});
</script>
</head>
<body>
<h1>单例模式测试</h1>
<div>计数: <span id="count">0</span></div>
<button id="increment">+1</button>
<button id="increment-5">+5 (批量)</button>
</body>
</html>
4.多例模式 ★ 重要·推荐
双例模式效果展示
双例模式封装
javascript
/**
* 多例模式状态管理工具
* 允许创建多个独立的状态管理实例,每个实例拥有独立的状态和订阅系统
*
* 主要特点:
* 1. 可创建多个隔离的状态管理实例
* 2. 每个实例拥有独立的useState和batch方法
* 3. 完整的状态不可变性保证
* 4. 支持批量更新优化性能
*
* 使用方式:
* const store = createStateStore();
* const counter = store.useState(0);
*
* counter.subscribe(state => console.log(state));
* counter.setState(prev => prev + 1);
* store.batch(() => { ... });
*/
// ==================== 深拷贝工具函数 ====================
/**
* 高性能深拷贝函数
* @param {any} obj - 需要拷贝的对象
* @param {WeakMap} [hash=new WeakMap()] - 用于存储已拷贝对象的WeakMap(防止循环引用)
* @returns {any} 深拷贝后的对象
*
* 实现特点:
* 1. 处理基本数据类型:直接返回
* 2. 处理循环引用:使用WeakMap缓存已拷贝对象
* 3. 保留特殊对象类型:Date、RegExp等
* 4. 原型链继承:保持原型链关系
* 5. 性能优化:使用Object.keys+Symbol属性遍历
*/
function deepClone(obj, hash = new WeakMap()) {
// 处理null和undefined
if (obj == null) return obj;
// 处理基本数据类型(string, number, boolean, symbol, bigint)
if (typeof obj !== 'object') return obj;
// 处理特殊对象类型
const constructor = obj.constructor;
const specialTypes = ['Date', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet'];
if (specialTypes.includes(constructor.name)) {
return new constructor(obj);
}
// 检查循环引用
if (hash.has(obj)) return hash.get(obj);
// 根据对象类型创建空对象或数组
const clone = Array.isArray(obj)
? []
: Object.create(Object.getPrototypeOf(obj));
// 缓存当前对象,防止循环引用
hash.set(obj, clone);
// 拷贝Symbol类型属性
const symKeys = Object.getOwnPropertySymbols(obj);
if (symKeys.length > 0) {
symKeys.forEach(symKey => {
clone[symKey] = deepClone(obj[symKey], hash);
});
}
// 拷贝普通属性
Object.keys(obj).forEach(key => {
clone[key] = deepClone(obj[key], hash);
});
return clone;
}
// ==================== 状态管理工厂函数 ====================
/**
* 创建新的状态管理实例
* @returns {Object} 包含useState和batch方法的对象
*
* 每个实例包含:
* 1. 独立的订阅者系统
* 2. 独立的批量更新队列
* 3. 独立的状态树
*/
export function createStateStore() {
/**
* 订阅者集合
* Map结构:
* key: 状态代理对象(Proxy)
* value: 该状态的订阅者回调集合(Set)
*/
const subscribers = new Map();
/**
* 批量更新队列
* 数组结构,每个元素包含:
* - proxy: 状态代理对象
* - updater: 更新函数或值
*/
let batchQueue = [];
/**
* 批量更新标志
* @type {boolean}
*/
let isBatching = false;
// ==================== 内部工具方法 ====================
/**
* 通知订阅者状态变更
* @param {Proxy} proxy - 状态代理对象
*
* 实现特点:
* 1. 使用微任务(Promise)异步执行通知
* 2. 错误处理避免影响其他订阅者
* 3. 自动清理无效订阅
*/
function batchNotify(proxy) {
// 获取当前状态的所有订阅者
const callbacks = subscribers.get(proxy);
if (!callbacks || callbacks.size === 0) return;
// 使用微任务异步执行通知
Promise.resolve().then(() => {
// 获取当前状态值
const state = proxy.value;
// 遍历执行所有订阅回调
callbacks.forEach(callback => {
try {
callback(state);
} catch (error) {
console.error('[状态通知错误] 订阅回调执行失败:', error);
}
});
});
}
// ==================== 公开API ====================
/**
* 创建响应式状态
* @param {any} initialState - 初始状态
* @returns {Object} 包含state、setState和subscribe方法的对象
*
* @throws {Error} 当initialState为undefined时抛出错误
*/
function useState(initialState) {
// 参数校验
if (typeof initialState === 'undefined') {
throw new Error('useState: 初始状态不能为undefined');
}
// 创建响应式代理对象
const proxy = new Proxy(
{ value: deepClone(initialState) },
{
/**
* 代理set陷阱
* @param {Object} target - 目标对象
* @param {string} key - 属性名
* @param {any} value - 新值
* @returns {boolean} 是否设置成功
*/
set(target, key, value) {
// 只处理value属性的变更
if (key !== 'value') return false;
// 深拷贝新值,确保状态不可变
target[key] = deepClone(value);
// 非批量模式下立即通知订阅者
if (!isBatching) {
batchNotify(proxy);
}
return true;
}
}
);
/**
* 订阅状态变更
* @param {Function} callback - 状态变更回调函数
* @returns {Function} 取消订阅的函数
*
* @throws {Error} 当callback不是函数时抛出错误
*/
function subscribe(callback) {
// 参数校验
if (typeof callback !== 'function') {
throw new Error('subscribe: 回调必须是函数');
}
// 初始化该状态的订阅者集合
if (!subscribers.has(proxy)) {
subscribers.set(proxy, new Set());
}
// 添加订阅者
const callbacks = subscribers.get(proxy);
callbacks.add(callback);
// 返回取消订阅函数
return function unsubscribe() {
callbacks.delete(callback);
// 清理空订阅集合
if (callbacks.size === 0) {
subscribers.delete(proxy);
}
};
}
/**
* 更新状态
* @param {Function|any} updater - 更新函数或新状态值
*
* 更新规则:
* 1. 如果是函数:updater(prevState) => newState
* 2. 如果是值:直接替换状态
*/
function setState(updater) {
if (isBatching) {
// 批量模式下将更新操作加入队列
batchQueue.push({
proxy,
updater
});
} else {
// 直接更新模式
proxy.value = typeof updater === 'function'
? updater(proxy.value)
: updater;
}
}
// 返回状态访问接口
return {
/**
* 获取当前状态值
* @returns {any} 当前状态
*/
get state() {
return proxy.value;
},
setState,
subscribe
};
}
/**
* 批量更新状态
* @param {Function} callback - 包含多个状态更新的回调函数
*
* 实现特点:
* 1. 合并多个setState调用为一次更新
* 2. 自动处理状态依赖关系
* 3. 最终只触发一次订阅通知
*/
function batch(callback) {
// 如果已经在批量模式中,直接执行回调
if (isBatching) {
callback();
return;
}
// 进入批量模式
isBatching = true;
batchQueue = [];
try {
// 执行用户回调,收集所有setState操作
callback();
// 按状态代理分组更新操作
const updatesByProxy = new Map();
batchQueue.forEach(({ proxy, updater }) => {
if (!updatesByProxy.has(proxy)) {
updatesByProxy.set(proxy, []);
}
updatesByProxy.get(proxy).push(updater);
});
// 处理每个状态代理的更新
updatesByProxy.forEach((updaters, proxy) => {
let currentState = proxy.value;
// 按顺序应用所有更新器
updaters.forEach(updater => {
currentState = typeof updater === 'function'
? updater(currentState)
: updater;
// 确保每次更新都是不可变的
currentState = deepClone(currentState);
});
// 最终更新状态值
proxy.value = currentState;
// 通知该状态的订阅者
batchNotify(proxy);
});
} finally {
// 确保无论是否出错都退出批量模式
isBatching = false;
batchQueue = [];
}
}
// 返回实例方法
return { useState, batch };
}
// ==================== 可选默认实例 ====================
/**
* 默认导出的状态管理实例
* 为方便使用,同时提供创建新实例和默认实例两种方式
*/
const defaultStore = createStateStore();
export const { useState: defaultUseState, batch: defaultBatch } = defaultStore;
双例模式HTML测试
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多例模式状态管理测试</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.counter {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 8px 16px;
margin-right: 10px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>多例模式状态管理测试</h1>
<div class="counter">
<h2>独立计数器1</h2>
<div>当前值: <span id="counter1-value">0</span></div>
<button id="counter1-increment">增加</button>
<button id="counter1-batch">批量增加(+3)</button>
</div>
<div class="counter">
<h2>独立计数器2</h2>
<div>当前值: <span id="counter2-value">100</span></div>
<button id="counter2-increment">增加</button>
</div>
<script type="module">
// 从模块导入创建方法
import { createStateStore } from './multi-state.js';
// 创建两个完全独立的状态管理实例
const store1 = createStateStore();
const store2 = createStateStore();
// 实例1:计数器1
const counter1 = store1.useState(0);
counter1.subscribe(state => {
document.getElementById('counter1-value').textContent = state;
console.log('计数器1更新:', state);
});
document.getElementById('counter1-increment').addEventListener('click', () => {
counter1.setState(prev => prev + 1);
});
document.getElementById('counter1-batch').addEventListener('click', () => {
store1.batch(() => {
counter1.setState(prev => prev + 1);
counter1.setState(prev => prev + 1);
counter1.setState(prev => prev + 1);
});
});
// 实例2:计数器2 (完全独立)
const counter2 = store2.useState(100);
counter2.subscribe(state => {
document.getElementById('counter2-value').textContent = state;
console.log('计数器2更新:', state);
});
document.getElementById('counter2-increment').addEventListener('click', () => {
counter2.setState(prev => prev + 10);
});
// 暴露到全局方便测试
window.stores = { store1, store2, counter1, counter2 };
</script>
<div style="margin-top: 30px; color: #666;">
<h3>测试说明:</h3>
<p>1. 两个计数器使用完全独立的状态管理实例</p>
<p>2. 打开控制台可以查看状态变化日志</p>
<p>3. 在控制台输入 <code>stores</code> 可以访问状态实例</p>
</div>
</body>
</html>
5.单例模式和双例模式的区别 ★ 了解
-
单例模式:
-
全局共享一个状态树
-
直接导出
useState
和batch
-
适合中小型应用
-
-
多例模式:
-
通过
createStateStore()
创建独立实例 -
每个实例有自己的状态和订阅系统
-
适合大型应用或需要隔离状态的场景
-
6.兼容性分析 ★ 了解
- 支持的浏览器
特性 | 最低支持版本 | 覆盖率 |
---|---|---|
Proxy | Chrome 49+ | ~98% |
Firefox 18+ | ||
Edge 12+ | ||
Safari 10+ | ||
WeakMap | IE 11+ | ~99% |
Promise (微任务) | ES6+ | ~98% |
- 不兼容场景
-
IE 11及以下 :不支持Proxy(可用
Object.defineProperty
降级) -
老旧移动浏览器:部分Android 4.x设备不支持ES6
- Polyfill方案
javascript
// 在入口文件添加以下polyfill
import 'core-js/stable'; // 提供Promise/WeakMap等
import 'proxy-polyfill'; // 提供Proxy的简单实现
7.全代码测试页 ★ 了解·测试
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高性能响应式状态管理</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
button {
padding: 8px 16px;
margin: 5px;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
.container {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
pre {
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
.perf-info {
color: #666;
font-size: 0.9em;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>高性能响应式状态管理</h1>
<!-- 计数器容器 -->
<div class="container">
<h2>性能计数器</h2>
<div>当前值: <span id="counter-value">0</span></div>
<div class="perf-info" id="counter-perf"></div>
<button id="increment">增加 (+1)</button>
<button id="increment-100">快速增加100次</button>
<button id="increment-1000">压力测试1000次</button>
</div>
<!-- 用户信息容器 -->
<div class="container">
<h2>用户信息</h2>
<pre id="user-info"></pre>
<div class="perf-info" id="user-perf"></div>
<button id="update-user">更新用户信息</button>
</div>
<!-- 待办事项容器 -->
<div class="container">
<h2>待办事项</h2>
<ul id="todo-list"></ul>
<input type="text" id="new-todo" placeholder="输入新事项">
<button id="add-todo">添加</button>
<div class="perf-info" id="todo-perf"></div>
</div>
<script>
/**
* 高性能深拷贝函数
* 特点:
* 1. 处理循环引用
* 2. 保留特殊对象类型(Date, RegExp等)
* 3. 惰性拷贝(按需拷贝)
* @param {any} obj - 需要拷贝的对象
* @param {WeakMap} hash - 用于存储已拷贝对象的WeakMap(防止循环引用)
* @return {any} 深拷贝后的对象
*/
function deepClone(obj, hash = new WeakMap()) {
// 基本类型直接返回
if (obj === null || typeof obj !== 'object') return obj;
// 处理循环引用
if (hash.has(obj)) return hash.get(obj);
// 处理特殊对象
const constructor = obj.constructor;
if (/^(Date|RegExp|Map|Set)$/i.test(constructor.name)) {
return new constructor(obj);
}
// 初始化克隆对象
const clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
hash.set(obj, clone);
// 使用Object.keys+forEach比for-in性能更好
Object.keys(obj).forEach(key => {
clone[key] = deepClone(obj[key], hash);
});
return clone;
}
/**
* 创建高性能状态管理Store
* @return {Object} 包含useState和batch方法的对象
*/
function createStore() {
const subscribers = new Map(); // 存储订阅者回调函数
let batchQueue = []; // 批量更新队列
let isBatching = false; // 是否处于批量更新模式
/**
* 批量执行回调(使用微任务节流)
* @param {Proxy} proxy - 状态代理对象
*/
function batchNotify(proxy) {
const callbacks = subscribers.get(proxy);
if (!callbacks) return;
// 使用微任务确保在UI更新前处理所有状态变更
Promise.resolve().then(() => {
const state = proxy.value;
callbacks.forEach(callback => {
try {
callback(state);
} catch (e) {
console.error('订阅回调出错:', e);
}
});
});
}
/**
* 创建响应式状态
* @param {any} initialState - 初始状态
* @return {Object} 包含state、setState和subscribe方法的对象
*/
function useState(initialState) {
// 验证初始状态
if (typeof initialState === 'undefined') {
throw new Error('Initial state cannot be undefined');
}
// 创建响应式代理
const proxy = new Proxy({ value: deepClone(initialState) }, {
set(target, key, value) {
if (key !== 'value') return false;
// 深拷贝新值
target[key] = deepClone(value);
// 非批量模式下立即通知
if (!isBatching) {
batchNotify(proxy);
}
return true;
}
});
/**
* 订阅状态变化
* @param {Function} callback - 状态变化时的回调函数
* @return {Function} 取消订阅的函数
*/
function subscribe(callback) {
if (typeof callback !== 'function') {
throw new Error('订阅回调必须是一个函数');
}
if (!subscribers.has(proxy)) {
subscribers.set(proxy, new Set());
}
const callbacks = subscribers.get(proxy);
callbacks.add(callback);
// 返回取消订阅函数
return () => {
callbacks.delete(callback);
if (callbacks.size === 0) {
subscribers.delete(proxy);
}
};
}
/**
* 更新状态
* @param {Function|any} updater - 更新函数或新状态
*/
function setState(updater) {
if (isBatching) {
// 批量模式下将更新器和代理存入队列
batchQueue.push({
proxy,
updater
});
} else {
// 直接更新
proxy.value = typeof updater === 'function'
? updater(proxy.value)
: updater;
}
}
return {
get state() { return proxy.value; }, // 获取当前状态
setState, // 更新状态方法
subscribe // 订阅状态变化方法
};
}
/**
* 批量更新
* @param {Function} callback - 包含多个状态更新的回调函数
*/
function batch(callback) {
isBatching = true;
batchQueue = []; // 清空队列
try {
callback(); // 执行回调,收集所有setState调用
// 将更新按对应的状态代理分组
const updatesByProxy = new Map();
batchQueue.forEach(({ proxy, updater }) => {
if (!updatesByProxy.has(proxy)) {
updatesByProxy.set(proxy, []);
}
updatesByProxy.get(proxy).push(updater);
});
// 处理每个代理的更新队列
updatesByProxy.forEach((updaters, proxy) => {
let currentState = proxy.value;
// 按顺序应用所有更新器函数
updaters.forEach(updater => {
currentState = typeof updater === 'function'
? updater(currentState) // 基于当前状态计算新值
: updater;
currentState = deepClone(currentState); // 深拷贝新状态
});
// 一次性更新代理的值
proxy.value = currentState;
// 手动触发通知
batchNotify(proxy);
});
} finally {
isBatching = false;
batchQueue = []; // 清空队列
}
}
return { useState, batch };
}
// ==================== 使用示例 ====================
const { useState, batch } = createStore();
/**
* 性能监控工具
* @param {string} name - 监控器名称
* @return {Object} 包含record和updateUI方法的对象
*/
function createPerfMonitor(name) {
let lastTime = performance.now();
let count = 0;
let totalTime = 0;
return {
/**
* 记录性能数据
* @return {Object} 包含duration、avg和count的性能数据
*/
record() {
const now = performance.now();
const duration = now - lastTime;
lastTime = now;
count++;
totalTime += duration;
return {
duration: duration.toFixed(2),
avg: (totalTime / count).toFixed(2),
count
};
},
/**
* 更新UI显示性能数据
* @param {string} elementId - 显示性能数据的元素ID
*/
updateUI(elementId) {
const perf = this.record();
document.getElementById(elementId).textContent =
`最近更新: ${perf.duration}ms | 平均: ${perf.avg}ms | 更新次数: ${perf.count}`;
}
};
}
// 1. 计数器状态
const counter = useState(0);
const counterPerf = createPerfMonitor('counter');
// 订阅计数器状态变化
counter.subscribe(count => {
document.getElementById('counter-value').textContent = count;
counterPerf.updateUI('counter-perf');
});
// 2. 用户信息状态
const user = useState({
name: '张三',
age: 25,
address: {
city: '北京',
street: '朝阳区'
},
createdAt: new Date()
});
const userPerf = createPerfMonitor('user');
// 订阅用户信息状态变化
user.subscribe(user => {
document.getElementById('user-info').textContent = JSON.stringify(user, null, 2);
userPerf.updateUI('user-perf');
});
// 3. 待办事项状态
const todos = useState([]);
const todoPerf = createPerfMonitor('todo');
// 订阅待办事项状态变化
todos.subscribe(todos => {
const listElement = document.getElementById('todo-list');
listElement.innerHTML = '';
todos.forEach((todo, index) => {
const li = document.createElement('li');
li.textContent = `${index + 1}. ${todo.text}`;
if (todo.completed) {
li.style.textDecoration = 'line-through';
li.style.color = '#888';
}
// 添加完成/取消按钮
const completeButton = document.createElement('button');
completeButton.textContent = todo.completed ? '取消' : '完成';
completeButton.style.marginLeft = '10px';
completeButton.onclick = () => {
todos.setState(prev => {
const newTodos = [...prev];
newTodos[index] = {
...newTodos[index],
completed: !newTodos[index].completed
};
return newTodos;
});
};
li.appendChild(completeButton);
listElement.appendChild(li);
});
todoPerf.updateUI('todo-perf');
});
// ==================== 事件绑定 ====================
// 计数器操作
document.getElementById('increment').addEventListener('click', () => {
counter.setState(c => c + 1);
});
document.getElementById('increment-100').addEventListener('click', () => {
batch(() => {
for (let i = 0; i < 100; i++) {
counter.setState(c => c + 1);
}
});
});
document.getElementById('increment-1000').addEventListener('click', () => {
batch(() => {
for (let i = 0; i < 1000; i++) {
counter.setState(c => c + 1);
}
});
});
// 用户信息操作
document.getElementById('update-user').addEventListener('click', () => {
user.setState(prev => ({
...prev,
age: prev.age + 1,
address: {
...prev.address,
street: `朝阳区${Math.floor(Math.random() * 100)}号`
},
updatedAt: new Date()
}));
});
// 待办事项操作
document.getElementById('add-todo').addEventListener('click', () => {
const input = document.getElementById('new-todo');
const text = input.value.trim();
if (text) {
todos.setState(prev => [
...prev,
{ text, completed: false }
]);
input.value = '';
}
});
// 初始化输入框回车事件
document.getElementById('new-todo').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
document.getElementById('add-todo').click();
}
});
</script>
</body>
</html>