如何更简洁优雅地用原生JS实现井字棋游戏棋盘的HTML生成
棋盘生成逻辑优化方案
核心优化方向
- 职责解耦:将渲染逻辑独立封装为
BoardRenderer类,和负责业务规则的Board类拆分,后续修改渲染逻辑不会影响核心游戏规则 - 命名规范化:将语义模糊的
x/y单字母变量替换为row/col,变量名遵循JS小驼峰规范,提升可读性 - 放弃字符串拼接HTML:使用原生DOM API创建元素,避免XSS风险,结构层级更清晰
- 可配置化设计:支持自定义行类名、格子类名、ID前缀等参数,后续调整样式或交互无需修改底层渲染逻辑
- 内置交互支持:统一绑定格子点击事件,返回行列坐标,省去后续二次遍历DOM绑定事件的步骤
优化后代码实现
1. 原有Board类(无需修改)
class Board { #boardSize; #player1Symbol; #player2Symbol; constructor(boardSize = 3, player1Symbol = 'X', player2Symbol = 'O') { this.#boardSize = boardSize; this.#player1Symbol = player1Symbol; this.#player2Symbol = player2Symbol; } get boardSize() { return this.#boardSize; } get player1Symbol() { return this.#player1Symbol; } get player2Symbol() { return this.#player2Symbol; } }
2. 新增BoardRenderer渲染类
class BoardRenderer { constructor(board, containerSelector, options = {}) { this.board = board; this.container = document.querySelector(containerSelector); // 合并默认配置和自定义配置 this.options = Object.assign({ rowClassPrefix: 'row-', cellClass: 'game-cell', cellIdPrefix: 'btn-', onCellClick: null }, options); } render() { // 清空容器原有内容 this.container.innerHTML = ''; const size = this.board.boardSize; for (let row = 0; row < size; row++) { // 创建行元素 const rowEl = document.createElement('div'); rowEl.className = `${this.options.rowClassPrefix}${row}`; for (let col = 0; col < size; col++) { // 创建格子按钮 const cellBtn = document.createElement('input'); cellBtn.type = 'button'; cellBtn.className = this.options.cellClass; cellBtn.id = `${this.options.cellIdPrefix}${row}.${col}`; cellBtn.value = ' '; // 绑定点击事件 if (typeof this.options.onCellClick === 'function') { cellBtn.addEventListener('click', () => { this.options.onCellClick(row, col, cellBtn); }); } rowEl.appendChild(cellBtn); } this.container.appendChild(rowEl); } } }
3. 调用示例
// 初始化棋盘实例 const board = new Board(3, 'X', 'O'); // 初始化渲染实例,可传入自定义配置和点击回调 const renderer = new BoardRenderer(board, '.game-ui', { cellClass: 'tic-tac-toe-cell', onCellClick: (row, col, btn) => { console.log(`点击了第${row}行第${col}列的格子`, btn); // 这里可以直接写落子逻辑 } }); // 执行渲染 renderer.render();
4. HTML代码(无需修改)
<body> <div class="game-ui"></div> </body>
额外优化建议
- 可以给格子统一加通用类名,比如
game-cell,后续写CSS样式不用依赖行类名,选择器更简洁 - 如果后续要支持自定义棋盘尺寸,只需要修改
Board初始化时的参数,渲染逻辑无需调整 - 可以在
BoardRenderer中新增updateCell方法,封装落子后修改按钮内容的逻辑,进一步简化业务代码
内容的提问来源于stack exchange,提问作者Shadow
相关产品推荐
相关产品推荐

