无框架时代的JavaScript状态管理方法探究
无框架时代的Vanilla JavaScript状态与组件监控实现
你的示例代码已经抓住了无框架状态管理的核心——状态变更驱动视图更新,在React这类框架普及前,前端开发者确实是围绕这个核心思路,用多种轻量化方式实现状态与视图的绑定,下面是当年主流的几种实践模式:
一、手动订阅发布模式(观察者模式)
这是最经典的解耦方案,核心是把状态单独抽离成可订阅的管理器,状态更新时主动通知所有关联视图组件执行渲染:
// 全局/模块级状态管理器 const StateManager = { _state: {}, _subscribers: [], // 更新状态并通知订阅者 setState(newState) { this._state = { ...this._state, ...newState }; this._notifySubscribers(); }, // 订阅状态变更 subscribe(callback) { this._subscribers.push(callback); // 订阅后立即触发一次渲染 callback(this._state); }, _notifySubscribers() { this._subscribers.forEach(cb => cb(this._state)); } }; // 示例组件:订阅状态更新 function HeaderComponent() { const headerEl = document.createElement('header'); StateManager.subscribe(state => { headerEl.textContent = state.pageTitle || '默认页面标题'; }); return headerEl; }
这种模式适合多组件共享同一状态的场景,能有效降低状态与视图的耦合度。
二、基于Getter/Setter的组件内状态封装
这和你代码的思路高度一致,通过封装状态的读写方法,在状态变更时自动触发视图渲染。当年开发者常用ES5的Object.defineProperty实现通用的状态拦截,或是直接用ES6的类getter/setter简化逻辑:
class BaseComponent { constructor() { this._state = {}; this.rootEl = document.createElement('div'); } // 拦截状态赋值,自动触发渲染 set state(newState) { this._state = { ...this._state, ...newState }; this.render(); } get state() { return { ...this._state }; // 返回副本避免外部直接修改内部状态 } // 子类实现具体渲染逻辑 render() {} } class HomePage extends BaseComponent { constructor() { super(); this.displayEl = document.createElement('p'); this.rootEl.appendChild(this.displayEl); } render() { this.displayEl.textContent = JSON.stringify(this.state); } }
你的代码是这种模式的简化版,核心逻辑完全没问题,建议把状态更新改成合并式赋值(而非直接覆盖),避免丢失未更新的状态属性。
三、事件驱动的状态更新
对于小型项目或简单组件,开发者常直接通过DOM事件触发状态变更,再手动更新对应视图,逻辑直观且无需额外封装:
function TodoList() { let todos = []; const listEl = document.createElement('ul'); const inputEl = document.createElement('input'); inputEl.addEventListener('keypress', (e) => { if (e.key === 'Enter' && inputEl.value.trim()) { todos.push(inputEl.value.trim()); inputEl.value = ''; _renderTodos(); } }); function _renderTodos() { listEl.innerHTML = todos.map(todo => `<li>${todo}</li>`).join(''); } // 初始渲染 _renderTodos(); return { listEl, inputEl }; }
这种方式的缺点是状态与视图耦合度较高,适合逻辑简单的场景。
你的HomePage类实现已经很贴近当年的实践,只需小优化即可更完善:
- 将状态更新改为合并式:
this.#state = { ...this.#state, ...newValue },避免覆盖原有状态; - 可在
constructor中直接调用monitorState完成初始化,无需外部手动触发。
内容的提问来源于stack exchange,提问作者mike
相关产品推荐
相关产品推荐

