You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无框架时代的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类实现已经很贴近当年的实践,只需小优化即可更完善:

  1. 将状态更新改为合并式:this.#state = { ...this.#state, ...newValue },避免覆盖原有状态;
  2. 可在constructor中直接调用monitorState完成初始化,无需外部手动触发。

内容的提问来源于stack exchange,提问作者mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 04:53:16