Search K
Appearance
Appearance
📊 SEO元描述:2024年最新JavaScript简单状态管理教程,详解观察者模式和发布-订阅模式实现。包含完整代码示例和实战应用,适合前端开发者快速掌握状态管理设计模式。
核心关键词:JavaScript简单状态管理 2024、观察者模式实现、发布订阅模式、JavaScript设计模式、前端状态管理模式
长尾关键词:JavaScript观察者模式怎么实现、发布订阅模式和观察者模式区别、前端状态管理设计模式、JavaScript事件驱动编程
通过本节JavaScript简单状态管理详解,你将系统性掌握:
简单状态管理是现代前端开发的基础技能,通过观察者模式和发布-订阅模式实现松耦合的组件通信,也是大型状态管理库的底层原理。
💡 设计思想:优秀的状态管理不在于复杂的API,而在于清晰的设计模式和合理的架构
观察者模式定义了对象间的一对多依赖关系,当主题对象状态改变时,所有依赖它的观察者都会收到通知:
// 🎯 观察者模式基础实现
class Subject {
constructor() {
this.observers = [];
this.state = {};
}
// 添加观察者
addObserver(observer) {
if (typeof observer.update === 'function') {
this.observers.push(observer);
} else {
throw new Error('Observer must have an update method');
}
}
// 移除观察者
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
// 通知所有观察者
notifyObservers(data) {
this.observers.forEach(observer => {
observer.update(data, this.state);
});
}
// 设置状态并通知观察者
setState(newState) {
const prevState = { ...this.state };
this.state = { ...this.state, ...newState };
this.notifyObservers({
type: 'STATE_CHANGE',
payload: newState,
prevState,
currentState: this.state
});
}
// 获取当前状态
getState() {
return { ...this.state };
}
}
// 观察者基类
class Observer {
constructor(name) {
this.name = name;
}
update(data, state) {
console.log(`${this.name} received update:`, data);
}
}
// 使用示例
const userStore = new Subject();
// 创建观察者
const headerComponent = new Observer('Header');
headerComponent.update = function(data, state) {
console.log(`Header updating user display: ${state.user?.name}`);
this.render(state.user);
};
const sidebarComponent = new Observer('Sidebar');
sidebarComponent.update = function(data, state) {
console.log(`Sidebar updating navigation: ${state.user?.role}`);
this.updateNavigation(state.user);
};
// 注册观察者
userStore.addObserver(headerComponent);
userStore.addObserver(sidebarComponent);
// 状态变化,自动通知所有观察者
userStore.setState({
user: { name: 'John Doe', role: 'admin' }
});// 🚀 高级观察者模式:支持选择性订阅
class AdvancedSubject {
constructor() {
this.observers = new Map(); // 使用Map存储观察者和其订阅的事件类型
this.state = {};
}
// 订阅特定类型的状态变化
subscribe(eventType, observer, options = {}) {
if (!this.observers.has(eventType)) {
this.observers.set(eventType, []);
}
const subscription = {
observer,
options,
id: this.generateId()
};
this.observers.get(eventType).push(subscription);
// 返回取消订阅函数
return () => this.unsubscribe(eventType, subscription.id);
}
// 取消订阅
unsubscribe(eventType, subscriptionId) {
if (this.observers.has(eventType)) {
const subscriptions = this.observers.get(eventType);
const index = subscriptions.findIndex(sub => sub.id === subscriptionId);
if (index > -1) {
subscriptions.splice(index, 1);
}
}
}
// 通知特定类型的观察者
notify(eventType, data) {
if (this.observers.has(eventType)) {
const subscriptions = this.observers.get(eventType);
subscriptions.forEach(subscription => {
const { observer, options } = subscription;
// 支持条件过滤
if (options.filter && !options.filter(data)) {
return;
}
// 支持防抖
if (options.debounce) {
this.debounce(
() => observer.update(data, this.state),
options.debounce,
subscription.id
);
} else {
observer.update(data, this.state);
}
});
}
}
// 更新状态
updateState(path, value, eventType = 'STATE_CHANGE') {
const prevState = { ...this.state };
this.setNestedValue(this.state, path, value);
this.notify(eventType, {
path,
value,
prevState,
currentState: this.state
});
}
// 批量更新状态
batchUpdate(updates) {
const prevState = { ...this.state };
updates.forEach(({ path, value }) => {
this.setNestedValue(this.state, path, value);
});
this.notify('BATCH_UPDATE', {
updates,
prevState,
currentState: this.state
});
}
// 辅助方法
generateId() {
return Math.random().toString(36).substr(2, 9);
}
setNestedValue(obj, path, value) {
const keys = path.split('.');
const lastKey = keys.pop();
const target = keys.reduce((current, key) => {
if (!(key in current)) current[key] = {};
return current[key];
}, obj);
target[lastKey] = value;
}
// 防抖实现
debounce(func, wait, key) {
if (!this.debounceTimers) this.debounceTimers = new Map();
if (this.debounceTimers.has(key)) {
clearTimeout(this.debounceTimers.get(key));
}
const timer = setTimeout(() => {
func();
this.debounceTimers.delete(key);
}, wait);
this.debounceTimers.set(key, timer);
}
}
// 使用示例
const appStore = new AdvancedSubject();
// 创建专门的观察者
class ComponentObserver {
constructor(name, renderFunction) {
this.name = name;
this.renderFunction = renderFunction;
}
update(data, state) {
console.log(`${this.name} received:`, data);
if (this.renderFunction) {
this.renderFunction(state);
}
}
}
// 订阅用户相关状态变化
const userObserver = new ComponentObserver('UserComponent', (state) => {
console.log('Rendering user info:', state.user);
});
const unsubscribeUser = appStore.subscribe('USER_UPDATE', userObserver, {
filter: (data) => data.path.startsWith('user.'),
debounce: 100
});
// 订阅主题变化
const themeObserver = new ComponentObserver('ThemeComponent', (state) => {
console.log('Applying theme:', state.ui.theme);
});
appStore.subscribe('THEME_CHANGE', themeObserver);
// 触发状态更新
appStore.updateState('user.name', 'Alice', 'USER_UPDATE');
appStore.updateState('user.email', 'alice@example.com', 'USER_UPDATE');
appStore.updateState('ui.theme', 'dark', 'THEME_CHANGE');发布-订阅模式通过消息中心解耦发布者和订阅者,实现更灵活的事件通信:
// 📢 发布-订阅模式基础实现
class EventBus {
constructor() {
this.events = new Map();
this.maxListeners = 10; // 防止内存泄漏
}
// 订阅事件
on(eventName, listener, options = {}) {
if (!this.events.has(eventName)) {
this.events.set(eventName, []);
}
const listeners = this.events.get(eventName);
// 检查监听器数量限制
if (listeners.length >= this.maxListeners) {
console.warn(`Event '${eventName}' has reached max listeners limit`);
}
const subscription = {
listener,
options,
id: this.generateId(),
once: options.once || false
};
listeners.push(subscription);
// 返回取消订阅函数
return () => this.off(eventName, subscription.id);
}
// 一次性订阅
once(eventName, listener, options = {}) {
return this.on(eventName, listener, { ...options, once: true });
}
// 取消订阅
off(eventName, subscriptionId) {
if (this.events.has(eventName)) {
const listeners = this.events.get(eventName);
const index = listeners.findIndex(sub => sub.id === subscriptionId);
if (index > -1) {
listeners.splice(index, 1);
// 如果没有监听器了,删除事件
if (listeners.length === 0) {
this.events.delete(eventName);
}
}
}
}
// 发布事件
emit(eventName, ...args) {
if (!this.events.has(eventName)) {
return false;
}
const listeners = this.events.get(eventName);
const toRemove = [];
listeners.forEach((subscription, index) => {
const { listener, options, once } = subscription;
try {
// 支持异步监听器
if (options.async) {
Promise.resolve(listener(...args)).catch(error => {
console.error(`Async listener error for event '${eventName}':`, error);
});
} else {
listener(...args);
}
// 一次性监听器标记为删除
if (once) {
toRemove.push(index);
}
} catch (error) {
console.error(`Listener error for event '${eventName}':`, error);
}
});
// 移除一次性监听器
toRemove.reverse().forEach(index => {
listeners.splice(index, 1);
});
return true;
}
// 获取事件监听器数量
listenerCount(eventName) {
return this.events.has(eventName) ? this.events.get(eventName).length : 0;
}
// 获取所有事件名称
eventNames() {
return Array.from(this.events.keys());
}
// 清除所有监听器
removeAllListeners(eventName) {
if (eventName) {
this.events.delete(eventName);
} else {
this.events.clear();
}
}
// 设置最大监听器数量
setMaxListeners(n) {
this.maxListeners = n;
}
// 生成唯一ID
generateId() {
return Math.random().toString(36).substr(2, 9);
}
}
// 使用示例
const eventBus = new EventBus();
// 订阅用户登录事件
const unsubscribeLogin = eventBus.on('user:login', (user) => {
console.log('User logged in:', user.name);
// 更新UI显示
updateHeaderUserInfo(user);
});
// 订阅用户登出事件(一次性)
eventBus.once('user:logout', () => {
console.log('User logged out');
clearUserSession();
});
// 异步事件处理
eventBus.on('data:fetch', async (url) => {
const data = await fetch(url).then(res => res.json());
eventBus.emit('data:received', data);
}, { async: true });
// 发布事件
eventBus.emit('user:login', { name: 'John', id: 1 });
eventBus.emit('data:fetch', '/api/users');// 🏗️ 基于发布-订阅模式的状态管理器
class PubSubStateManager {
constructor(initialState = {}) {
this.state = { ...initialState };
this.eventBus = new EventBus();
this.middleware = [];
this.history = [{ ...initialState }];
this.maxHistorySize = 50;
}
// 添加中间件
use(middleware) {
this.middleware.push(middleware);
}
// 获取状态
getState() {
return { ...this.state };
}
// 订阅状态变化
subscribe(selector, callback, options = {}) {
const eventName = 'state:change';
return this.eventBus.on(eventName, (changeInfo) => {
const { prevState, currentState } = changeInfo;
// 如果提供了选择器,只在相关状态变化时触发
if (selector) {
const prevValue = selector(prevState);
const currentValue = selector(currentState);
if (prevValue !== currentValue) {
callback(currentValue, prevValue, changeInfo);
}
} else {
callback(currentState, prevState, changeInfo);
}
}, options);
}
// 派发动作
dispatch(action) {
const prevState = { ...this.state };
// 执行中间件
let processedAction = action;
for (const middleware of this.middleware) {
processedAction = middleware(processedAction, this.state) || processedAction;
}
// 更新状态
const newState = this.reducer(this.state, processedAction);
if (newState !== this.state) {
this.state = newState;
// 添加到历史记录
this.addToHistory(newState);
// 发布状态变化事件
this.eventBus.emit('state:change', {
action: processedAction,
prevState,
currentState: this.state
});
// 发布特定动作事件
this.eventBus.emit(`action:${processedAction.type}`, {
action: processedAction,
prevState,
currentState: this.state
});
}
}
// 状态归约器
reducer(state, action) {
switch (action.type) {
case 'SET_USER':
return {
...state,
user: action.payload
};
case 'UPDATE_USER_FIELD':
return {
...state,
user: {
...state.user,
[action.field]: action.value
}
};
case 'SET_THEME':
return {
...state,
ui: {
...state.ui,
theme: action.payload
}
};
case 'TOGGLE_SIDEBAR':
return {
...state,
ui: {
...state.ui,
sidebarOpen: !state.ui.sidebarOpen
}
};
default:
return state;
}
}
// 添加到历史记录
addToHistory(state) {
this.history.push({ ...state });
// 限制历史记录大小
if (this.history.length > this.maxHistorySize) {
this.history.shift();
}
}
// 时间旅行
timeTravel(stepIndex) {
if (stepIndex >= 0 && stepIndex < this.history.length) {
const prevState = { ...this.state };
this.state = { ...this.history[stepIndex] };
this.eventBus.emit('state:change', {
action: { type: 'TIME_TRAVEL', stepIndex },
prevState,
currentState: this.state
});
}
}
// 获取历史记录
getHistory() {
return [...this.history];
}
}
// 中间件示例
const loggerMiddleware = (action, state) => {
console.group(`Action: ${action.type}`);
console.log('Payload:', action.payload);
console.log('Previous State:', state);
console.groupEnd();
return action;
};
const asyncMiddleware = (action, state) => {
if (action.type.endsWith('_ASYNC')) {
// 处理异步动作
const baseType = action.type.replace('_ASYNC', '');
// 发起异步操作
action.asyncOperation()
.then(result => {
store.dispatch({
type: `${baseType}_SUCCESS`,
payload: result
});
})
.catch(error => {
store.dispatch({
type: `${baseType}_ERROR`,
payload: error.message
});
});
// 返回loading状态的动作
return {
type: `${baseType}_LOADING`,
payload: true
};
}
return action;
};
// 使用示例
const store = new PubSubStateManager({
user: null,
ui: {
theme: 'light',
sidebarOpen: false
}
});
// 添加中间件
store.use(loggerMiddleware);
store.use(asyncMiddleware);
// 订阅用户状态变化
const unsubscribeUser = store.subscribe(
state => state.user,
(user, prevUser) => {
console.log('User changed:', { prevUser, user });
}
);
// 订阅主题变化
store.subscribe(
state => state.ui.theme,
(theme) => {
document.body.className = `theme-${theme}`;
}
);
// 派发动作
store.dispatch({
type: 'SET_USER',
payload: { name: 'Alice', id: 1 }
});
store.dispatch({
type: 'SET_THEME',
payload: 'dark'
});
store.dispatch({
type: 'TOGGLE_SIDEBAR'
});// 📊 两种模式的对比实现
class PatternComparison {
// 观察者模式特点
static observerPatternDemo() {
// 直接耦合:观察者直接注册到主题
class NewsAgency {
constructor() {
this.observers = [];
this.news = '';
}
// 观察者直接注册
addObserver(observer) {
this.observers.push(observer);
}
// 直接通知所有观察者
setNews(news) {
this.news = news;
this.observers.forEach(observer => {
observer.update(news); // 直接调用观察者方法
});
}
}
class NewsChannel {
constructor(name) {
this.name = name;
}
update(news) {
console.log(`${this.name} broadcasting: ${news}`);
}
}
// 使用:观察者和主题直接关联
const agency = new NewsAgency();
const cnn = new NewsChannel('CNN');
const bbc = new NewsChannel('BBC');
agency.addObserver(cnn);
agency.addObserver(bbc);
agency.setNews('Breaking News!');
}
// 发布-订阅模式特点
static pubSubPatternDemo() {
// 松耦合:通过消息中心解耦
class MessageBroker {
constructor() {
this.subscribers = new Map();
}
// 订阅特定主题
subscribe(topic, callback) {
if (!this.subscribers.has(topic)) {
this.subscribers.set(topic, []);
}
this.subscribers.get(topic).push(callback);
}
// 发布到特定主题
publish(topic, data) {
if (this.subscribers.has(topic)) {
this.subscribers.get(topic).forEach(callback => {
callback(data);
});
}
}
}
// 发布者不知道订阅者的存在
class NewsPublisher {
constructor(broker) {
this.broker = broker;
}
publishNews(category, news) {
this.broker.publish(`news:${category}`, news);
}
}
// 订阅者不知道发布者的存在
class NewsSubscriber {
constructor(name, broker) {
this.name = name;
this.broker = broker;
}
subscribeToCategory(category) {
this.broker.subscribe(`news:${category}`, (news) => {
console.log(`${this.name} received ${category} news: ${news}`);
});
}
}
// 使用:发布者和订阅者完全解耦
const broker = new MessageBroker();
const publisher = new NewsPublisher(broker);
const subscriber1 = new NewsSubscriber('Reader1', broker);
const subscriber2 = new NewsSubscriber('Reader2', broker);
subscriber1.subscribeToCategory('sports');
subscriber2.subscribeToCategory('tech');
publisher.publishNews('sports', 'Team wins championship!');
publisher.publishNews('tech', 'New framework released!');
}
// 对比总结
static getComparison() {
return {
observerPattern: {
coupling: '紧耦合 - 观察者直接依赖主题',
communication: '直接通信 - 主题直接调用观察者方法',
flexibility: '较低 - 观察者必须实现特定接口',
scalability: '有限 - 难以支持复杂的事件路由',
useCase: '简单的一对多通知场景',
example: 'Model-View架构中的数据绑定'
},
pubSubPattern: {
coupling: '松耦合 - 发布者和订阅者互不知晓',
communication: '间接通信 - 通过消息中心转发',
flexibility: '高 - 支持任意类型的消息和处理器',
scalability: '强 - 支持复杂的事件路由和过滤',
useCase: '复杂的事件驱动系统',
example: '微服务架构中的事件总线'
}
};
}
}// 🎯 场景选择指南
class ScenarioGuide {
// 适合观察者模式的场景
static observerScenarios() {
return {
// 场景1:数据绑定
dataBinding: {
description: 'Model变化时自动更新View',
implementation: class DataBinding {
constructor() {
this.model = new Subject();
this.views = [];
}
addView(view) {
this.model.addObserver(view);
this.views.push(view);
}
updateModel(data) {
this.model.setState(data);
}
}
},
// 场景2:组件生命周期
componentLifecycle: {
description: '组件状态变化时通知相关组件',
implementation: class ComponentManager {
constructor() {
this.component = new Subject();
}
mount() {
this.component.notifyObservers({ type: 'MOUNTED' });
}
unmount() {
this.component.notifyObservers({ type: 'UNMOUNTED' });
}
}
}
};
}
// 适合发布-订阅模式的场景
static pubSubScenarios() {
return {
// 场景1:跨模块通信
crossModuleCommunication: {
description: '不同模块间的松耦合通信',
implementation: class ModuleSystem {
constructor() {
this.eventBus = new EventBus();
}
loadModule(module) {
module.init(this.eventBus);
this.eventBus.emit('module:loaded', module.name);
}
}
},
// 场景2:用户行为追踪
userTracking: {
description: '用户行为事件的收集和处理',
implementation: class Analytics {
constructor() {
this.eventBus = new EventBus();
this.setupTracking();
}
setupTracking() {
this.eventBus.on('user:click', this.trackClick);
this.eventBus.on('user:pageview', this.trackPageView);
this.eventBus.on('user:purchase', this.trackPurchase);
}
trackClick(data) {
console.log('Click tracked:', data);
}
trackPageView(data) {
console.log('Page view tracked:', data);
}
trackPurchase(data) {
console.log('Purchase tracked:', data);
}
}
}
};
}
}通过本节JavaScript简单状态管理详解的学习,你已经掌握:
A: 观察者模式性能更好,因为是直接调用,没有中间层开销。但发布-订阅模式提供了更好的灵活性和可扩展性,适合复杂场景。
A: 及时取消订阅、使用WeakMap存储引用、设置监听器数量限制、在组件销毁时清理订阅关系。
A: 简单状态管理适合中小型项目或作为学习基础。大型项目建议使用成熟的状态管理库,它们提供了更多高级功能和优化。
A: 可以通过中间件模式处理异步操作,或者使用Promise/async-await结合事件发布来管理异步状态。
A: 维护状态历史记录数组,每次状态变更时保存快照,实现时间旅行功能来支持撤销/重做。
// 问题:忘记取消订阅导致内存泄漏
// 解决:实现自动清理机制
class AutoCleanupEventBus extends EventBus {
constructor() {
super();
this.componentSubscriptions = new WeakMap();
}
subscribeComponent(component, eventName, listener) {
const unsubscribe = this.on(eventName, listener);
if (!this.componentSubscriptions.has(component)) {
this.componentSubscriptions.set(component, []);
}
this.componentSubscriptions.get(component).push(unsubscribe);
return unsubscribe;
}
cleanupComponent(component) {
const subscriptions = this.componentSubscriptions.get(component);
if (subscriptions) {
subscriptions.forEach(unsubscribe => unsubscribe());
this.componentSubscriptions.delete(component);
}
}
}// 问题:并发状态更新导致数据丢失
// 解决:实现状态更新队列
class QueuedStateManager {
constructor() {
this.state = {};
this.updateQueue = [];
this.isProcessing = false;
}
async updateState(updater) {
return new Promise((resolve) => {
this.updateQueue.push({ updater, resolve });
this.processQueue();
});
}
async processQueue() {
if (this.isProcessing) return;
this.isProcessing = true;
while (this.updateQueue.length > 0) {
const { updater, resolve } = this.updateQueue.shift();
this.state = updater(this.state);
resolve(this.state);
}
this.isProcessing = false;
}
}"掌握观察者模式和发布-订阅模式是构建优秀状态管理系统的基础,通过本节学习,你已经具备了设计和实现简单状态管理的能力。继续深入学习状态持久化和现代状态管理技术,构建更强大的应用架构!"