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

如何在AlpineJS模块中使用JS类解决Todo实例复用问题

AlpineJS 结合 Todo 模块的实例复用问题解决

问题背景

使用函数式 Todo 模块结合 AlpineJS 时,添加多个待办项会出现所有条目同步更新的问题;改用类模块时,又无法正常运行,具体问题如下:

类导出的问题

  • 直接使用 Alpine.data('todo', Todo) 时,报错:Uncaught TypeError: Class constructor default cannot be invoked without 'new'
  • 尝试 x-data="new todo()" 时,报错:todo is not a constructor
  • 尝试 Alpine.data('todo', new Todo()); 时,报错:Uncaught TypeError: callback.bind is not a function

函数导出的问题

  • 仅推入 item.name 能生成独立条目,但业务需要完整的待办对象
  • 使用 structuredClone(item) 推入时,报错:Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': # could not be cloned.

解决方案

方案一:修复函数式模块的实例复用问题

核心问题是每次推入的都是同一个 item 对象引用,需在提交时创建新对象副本,避免引用复用。

  1. 修改 todo.js 的函数导出:
export default () => ({
  item: {
    name: '',
    completed: false
  },
  // 新增创建新待办项的方法
  createNewTodo() {
    // 复制当前item属性到新对象,切断引用
    const newTodo = { ...this.item };
    // 重置输入框内容
    this.item.name = '';
    return newTodo;
  }
});
  1. 修改 HTML 中的提交逻辑:
<article x-data="{ todos: [] }">
  <form x-data="todo" @submit.prevent="todos.push(createNewTodo())">
    <input x-model="item.name"><button>Add</button>
  </form>

  <ul>
    <template x-for="todo in todos" :key="todo">
      <li x-text="todo.name"></li>
    </template>
  </ul>
</article>

方案二:修复类模块的使用方式

AlpineJS 的 Alpine.data 要求接收一个返回对象的函数,而非直接传入类或类实例,需调整类的注册方式。

  1. 修改 todo.js 的类导出:
export default class Todo {
  constructor() {
    // 初始化空的待办项模板
    this.item = {
      name: '',
      completed: false
    };
  }

  createNewTodo() {
    const newTodo = { ...this.item };
    this.item.name = '';
    return newTodo;
  }
}
  1. 修改 Alpine 的注册逻辑:
<script type="module">
  import Alpine from './modules/alpine.js';
  import Todo from './modules/todo.js';

  // 用函数包裹类,确保每次x-data调用都生成新的类实例
  Alpine.data('todo', () => new Todo());
  Alpine.start();
</script>
  1. HTML 部分与方案一的提交逻辑一致:
<article x-data="{ todos: [] }">
  <form x-data="todo" @submit.prevent="todos.push(createNewTodo())">
    <input x-model="item.name"><button>Add</button>
  </form>

  <ul>
    <template x-for="todo in todos" :key="todo">
      <li x-text="todo.name"></li>
    </template>
  </ul>
</article>

关键说明

  • 函数式方案中,使用对象扩展运算符 { ...this.item } 创建副本,彻底切断原对象的引用关系;
  • 类方案中,Alpine.data 必须传入返回类实例的函数,保证每个 x-data="todo" 都生成独立的类实例;
  • 两种方案都需在提交后重置输入框的 item.name,避免下一次输入影响已添加的条目。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:07:39