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

JS主要操作DOM元素时可遵循的模式或Class风格规范有哪些?

DOM与全局变量冗余的表单代码优化方案

第一步:解决DOM查询冗余、集中管理引用

你现有代码里的重复DOM查询、零散选择器是最优先要优化的点,首先可以把所有选择器集中管理,同时做DOM缓存,避免重复查询DOM:

// 所有选择器统一存在常量对象里,修改时只需要改这一处
const FORM_SELECTORS = {
  formContrato: '#form_contrato',
  btnRespAluno: '#label_checkbox_resp_aluno',
  checkCombo: '#check_combo',
  cursoNome: '#curso_nome',
  comboCurso1: '#combo_curso_1',
  comboCurso2: '#combo_curso_2',
  valor: '#curso_valor',
  desconto: '#curso_desconto',
  total: '#curso_total',
  // 其余所有选择器都按这个格式补充
}

// 一次性获取所有DOM并缓存,避免重复调用querySelector
const $formDom = Object.fromEntries(
  Object.entries(FORM_SELECTORS).map(([key, selector]) => [key, document.querySelector(selector)])
)

后续使用时直接通过$formDom.formContrato、$formDom.valor即可调用对应的DOM元素,完全消除零散的querySelector,同时也能避免你示例中#curso_nome被重复查询两次的冗余问题。

第二步:用Class封装整表逻辑,消除全局变量

你的场景属于典型的单表单逻辑单元,非常适合用Class做封装,所有事件绑定、工具方法、业务逻辑都收敛在Class内部,不会污染全局作用域:

class ContratoForm {
  // 常量选择器作为静态属性
  static SELECTORS = {
    form: '#form_contrato',
    btnRespAluno: '#label_checkbox_resp_aluno',
    checkCombo: '#check_combo',
    cursoNome: '#curso_nome',
    // 其余选择器补充在这里
  }
  static MASK_RULES = [
    { selector: '#curso_valor', type: 'money' },
    { selector: '#curso_desconto', type: 'money' },
    { selector: '#resp_cep', pattern: '99999-999' },
    // 其余掩码规则补充在这里
  ]

  constructor() {
    // 初始化缓存DOM
    this.$dom = Object.fromEntries(
      Object.entries(ContratoForm.SELECTORS).map(([k, s]) => [k, document.querySelector(s)])
    )
    // 统一绑定事件
    this.bindEvents()
    // 统一初始化掩码
    this.initMasks()
    // 可在这里初始化表单状态
    this.state = {
      selectedCourse: null,
      discount: 0,
      total: 0
    }
  }

  bindEvents() {
    this.$dom.form.addEventListener('submit', (e) => this.handleSubmit(e))
    this.$dom.btnRespAluno.addEventListener('input', (e) => this.handleRespAlunoChange(e))
    this.$dom.checkCombo.addEventListener('input', (e) => this.handleComboChange(e))
    this.$dom.cursoNome.addEventListener('change', (e) => this.updateCourseInfo(e))
    this.$dom.valor.addEventListener('input', () => this.calcTotal())
    this.$dom.desconto.addEventListener('input', () => this.calcTotal())
    // 其余事件绑定补充在这里
  }

  initMasks() {
    ContratoForm.MASK_RULES.forEach(rule => {
      const el = document.querySelector(rule.selector)
      rule.type === 'money' ? VMasker(el).maskMoney() : VMasker(el).maskPattern(rule.pattern)
    })
  }

  // 原来的全局工具方法都转为实例方法,不会污染全局
  calcTotal() {
    const valor = Number(this.$dom.valor.value.replace(/\D/g, '')) / 100
    const desconto = Number(this.$dom.desconto.value.replace(/\D/g, '')) / 100
    this.state.total = valor - desconto
    this.$dom.total.value = this.state.total.toFixed(2)
  }

  handleSubmit(e) {
    e.preventDefault()
    // 表单提交逻辑
  }

  updateCourseInfo() {
    // 课程信息更新逻辑
  }
  // 其余业务方法都放在这里
}

// 全局只需要初始化一个实例即可
new ContratoForm()

适用的设计模式

  • 单例/模块模式:整个表单只有一个实例,配合ES6模块导入导出的话,甚至可以完全消除全局变量,所有逻辑都收敛在模块内部
  • 中介者模式:如果后续表单联动逻辑复杂度上升,比如选择套餐要联动修改课程、修改折扣要联动修改文案等,可以用中介者集中管理所有联动逻辑,避免不同功能块之间互相依赖

代码拆分的正确原则

拆分函数、拆分文件的核心前提是按职责拆分,不要为了拆分而拆分:

  • 通用工具函数(和当前表单业务无关的字符串处理、格式化方法等)可以单独抽成工具文件,供多场景复用
  • 常量配置(选择器、掩码规则、默认值、接口地址等)可以单独抽成常量文件,统一管理修改入口
  • 接口请求逻辑单独抽离,和业务逻辑解耦
  • 核心表单逻辑如果行数不超过500行不需要拆分,放在同一个文件里维护成本更低

额外优化建议

  • 状态和DOM分离:不要依赖DOM存储业务状态,所有业务状态(比如当前选中的课程、折扣额、总价)统一存在实例的state属性中,状态变化时同步更新DOM,调试和维护成本更低
  • 动态元素优先用事件委托:如果后续有动态生成的表单项,不要每个都单独绑定事件,统一绑定在父级form元素上,减少内存占用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 09:54:04