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

如何用模块模式优化JS代码?模块DOM获取异常问题咨询

重构Gridster面条代码:模块模式问题与优化方案

为什么你的模块获取不到DOM元素?

你遇到的核心问题很明确:模块里的settings对象在脚本加载时就立即执行了DOM选择器,但此时浏览器还没渲染出HTML里的.gridster ul元素,所以$('.gridster ul')返回的是空集合。

你在控制台直接调用能拿到元素,是因为此时页面已经完全加载完成了;而模块的settings是在脚本解析阶段就赋值的,早于$(function(){})的DOM就绪事件,自然拿不到目标元素。

快速修复方案

把settings里的DOM选择器改成在init方法里赋值,等DOM就绪后再获取元素:

var gridsterModule = {
  settings: {}, // 先初始化空对象
  init: function () {
    // 到这里DOM已经就绪,再查询元素就没问题了
    this.settings.gridElement = $('.gridster ul li');
    this.settings.gridList = $('.gridster ul');
    this.simpleTest();
  },
  simpleTest: function () {
    console.log(this.settings.gridList); // 现在能拿到正确的元素集合了
  }
}

$(function () {
  gridsterModule.init();
});

或者更灵活一点,把settings写成一个返回对象的函数,每次调用才执行选择器:

var gridsterModule = {
  getSettings: function () {
    return {
      gridElement: $('.gridster ul li'),
      gridList: $('.gridster ul')
    };
  },
  init: function () {
    this.settings = this.getSettings();
    this.simpleTest();
  },
  simpleTest: function () {
    console.log(this.settings.gridList);
  }
}

模块模式的正确用法

模块模式的核心是封装逻辑、隔离作用域、避免全局污染,让你的Gridster代码更易维护。我推荐用闭包版的模块模式,它支持私有成员,更安全:

// 用闭包封装私有变量和方法,只暴露公共接口
var gridsterModule = (function($) {
  // 私有变量:外部无法直接访问,避免被篡改
  var _settings = {};
  
  // 私有方法:只在模块内部复用的逻辑
  var _refreshGridster = function() {
    $('.gridster').gridster().data('gridster').refresh();
  };

  // 公共接口:暴露给外部调用的方法
  return {
    init: function() {
      // 初始化时才获取DOM元素
      _settings.gridElement = $('.gridster ul li');
      _settings.gridList = $('.gridster ul');
      // 可以在这里绑定模块相关的事件,进一步封装
      this.bindEvents();
    },
    bindEvents: function() {
      // 把事件绑定也放进模块,避免全局散落的事件监听
      $('.add').on('click', function() {
        gridsterModule.insertWidgetToGrid({content: '新组件'});
      });
    },
    applyJsonToGrid: function(gridData) {
      // 把JSON数据渲染到网格的逻辑
      _settings.gridList.empty();
      gridData.forEach(function(widget) {
        var $widget = $('<li>' + widget.content + '</li>');
        _settings.gridList.append($widget);
      });
      _refreshGridster();
    },
    insertWidgetToGrid: function(widgetOptions) {
      // 插入单个组件的逻辑,支持自定义参数
      var defaultOpts = {content: '默认组件', sizeX:1, sizeY:1};
      var opts = $.extend({}, defaultOpts, widgetOptions);
      var $widget = $('<li>' + opts.content + '</li>');
      _settings.gridList.append($widget);
      $('.gridster').gridster().data('gridster').add_widget($widget, opts.sizeX, opts.sizeY);
    }
  };
})(jQuery); // 传入jQuery,避免依赖全局变量,压缩时还能缩短变量名

// 页面就绪后初始化模块
$(function() {
  gridsterModule.init();
});

这个版本的优势:

  • 私有成员_settings和_refreshGridster不会被外部篡改,更安全
  • 所有Gridster相关的逻辑都集中在一个模块里,找代码、改逻辑都更方便
  • 把jQuery作为参数传入,减少全局依赖,代码更健壮

模块模式的注意事项

  • 永远不要在模块的顶层直接执行DOM操作,一定要等DOM就绪后(比如init方法里)再查询元素
  • 公共方法尽量保持单一职责:比如applyJsonToGrid只负责渲染JSON数据,insertWidgetToGrid只负责插入组件,不要一个方法做N件事
  • 常用的DOM元素要缓存起来,避免重复查询DOM(就像我们用_settings做的那样),能提升页面性能

其他Gridster代码优化建议

1. 优化事件委托

你之前用$(document).on("click", ".add", ...)的写法没问题,但如果.add元素有静态的父容器,建议把事件委托绑定到最近的静态父元素,减少事件冒泡的层级,提升性能:

// 假设.add的父元素是.grid-toolbar(页面加载时就存在)
$('.grid-toolbar').on('click', '.add', function() {
  gridsterModule.insertWidgetToGrid();
});

2. 抛弃全局函数

之前的applyJsonToGrid、insertWidgetToGrid都是全局函数,容易和其他代码冲突,全部放进模块里,通过模块的公共接口调用,减少全局变量污染。

3. 升级到ES6模块化(如果项目支持)

如果你的项目用了Webpack、Vite等构建工具,直接用ES6的import/export会更符合现代JS规范:

// gridsterModule.js
import $ from 'jquery';

let settings = {};

function _refreshGridster() {
  $('.gridster').gridster().data('gridster').refresh();
}

export function init() {
  settings.gridElement = $('.gridster ul li');
  settings.gridList = $('.gridster ul');
  bindEvents();
}

function bindEvents() {
  $('.add').on('click', () => insertWidgetToGrid({content: '新组件'}));
}

export function applyJsonToGrid(gridData) {
  // 渲染逻辑...
}

export function insertWidgetToGrid(widgetOptions) {
  // 插入逻辑...
}

// 主文件
import * as gridsterModule from './gridsterModule.js';

$(function() {
  gridsterModule.init();
});

4. 参数化函数,提升灵活性

比如insertWidgetToGrid可以接受组件的内容、大小、位置等参数,让函数能适应不同场景的需求,而不是硬编码固定内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:56:40