Livewire+Choices.js+AlpineJS报$nextTick重定义错误
问题根源
你遇到的Uncaught TypeError: Cannot redefine property: $nextTick错误和Choices组件崩溃,是多个逻辑错误叠加导致的:
- 触发报错的直接原因:全局变量污染。你先将
window.choices赋值为Choices构造函数,后续初始化实例时未做变量声明,直接给choices赋值(非严格模式下会自动挂载到window),把构造函数覆盖为实例,污染全局作用域,触发Alpine/Livewire的内部属性校验失败。 wire:model绑定错误:多选框被错误绑定到存储选项列表的$options属性上,而非存储选中值的$selectedOptions,选中选项时会直接篡改选项列表数据,触发组件异常重渲染。- 缺少实例销毁和重渲染同步逻辑:Livewire每次DOM重绘后,旧的Choices实例没有被销毁,Choices生成的自定义DOM不会和原生select同步,重复初始化、残留事件监听会导致状态完全错乱。
- 双向绑定断裂:Choices选中值变化时没有同步给Livewire,Livewire更新选中值后也没有同步回Choices实例。
修复方案
1. 修正Livewire组件类代码
移除冗余监听,修正事件派发逻辑,选中值更新时再通知前端同步:
<?php namespace App\Http\Livewire; use Livewire\Component; class ChoicesLivewire extends Component { /** * @var array */ public array $options; /** * @var array */ public array $selectedOptions = []; public function mount() { $collection = [ 1 => 'Architektur', 2 => 'Bauwirtschaft', 3 => 'Straßenbau', 4 => 'Tiefbau', ]; $this->options = $collection; $this->selectedOptions = [2]; } public function updatedSelectedOptions() { $this->dispatchBrowserEvent('choices:sync', ['currentSelects' => $this->selectedOptions]); } public function render() { return view('livewire.choices-livewire'); } }
2. 修正Blade模板代码
修正wire:model绑定目标,调整Alpine绑定逻辑,不需要手动给option加selected属性,Livewire会自动处理:
<div x-data="initChoices()"> <select wire:model="selectedOptions" x-ref="multipleSelect" x-init="init" multiple > @foreach($options as $key => $option) <option value="{{$key}}">{{$option}}</option> @endforeach </select> @dump($selectedOptions) </div>
3. 修正JS初始化代码
避免全局污染,增加实例销毁、双向同步、重渲染适配逻辑:
import Choices from "choices.js"; document.addEventListener('alpine:init', () => { Alpine.data('initChoices', () => ({ choicesInstance: null, init() { // 存在旧实例先销毁,避免重复初始化 if (this.choicesInstance) { this.choicesInstance.destroy(); } // 初始化Choices this.choicesInstance = new Choices(this.$refs.multipleSelect, { itemSelectText: '', removeItems: true, removeItemButton: true, }); // 设置初始选中值 this.choicesInstance.setChoiceByValue(this.$wire.entangle('selectedOptions').value.map(String)); // 监听Choices值变化,自动同步到Livewire,不需要手动写set逻辑 this.$refs.multipleSelect.addEventListener('change', () => { this.choicesInstance.setChoiceByValue(this.$wire.selectedOptions.map(String)); }); // 监听后端值更新事件,同步Choices选中状态 window.addEventListener('choices:sync', (event) => { this.choicesInstance.setChoiceByValue(event.detail.currentSelects.map(String)); }); // Livewire DOM更新完成后重建实例,避免DOM和实例脱节 this.$wire.on('dom-diffed', () => { if (this.choicesInstance) { this.choicesInstance.destroy(); this.init(); } }); } })) })
如果你使用的Alpine版本低于3.x,把
alpine:init事件替换为livewire:load即可。
内容的提问来源于stack exchange,提问作者fefe
相关产品推荐
相关产品推荐

