Laravel Livewire中Virtual Select多选组件选中后消失问题
Laravel Livewire + Virtual Select 多选组件点击后消失问题修复
问题场景
开发Laravel 10项目时,基于Virtual Select实现多选功能,修改代码后组件可正常显示,但点击选项后组件直接消失,浏览器控制台存在错误提示。使用版本:
- Laravel框架:
^10.48.4 - Livewire:
^3.4.9
组件代码
<?php namespace App\Livewire\Admin; use App\Models\Permission; use Illuminate\Support\Str; use Livewire\Component; class UsersPermissionsEditor extends Component { public $selectedPermissions= []; public $permissionsListing= []; public function render() { $this->permissionsListing = Permission::query()->get()/*->pluck('name', 'id')*/ ->map(function ($selectedPermissionItem) { return ['id' => $selectedPermissionItem->id, 'label' => Str::replace('_', ' ', Str::title($selectedPermissionItem->name))]; }) ->toArray(); return view('livewire.admin.users-permissions-editor'); } }
Blade视图代码
<div class="admin_page_container" id="users_permissions_editor_admin_page_container"> <div class="editor_listing_wrapper_bix_width" x-data="adminUsersPermissionsEditorComponent()" x-init="[onAdminUsersPermissionsEditorComponentInit() ]" x-cloak> $selectedPermissions::{{ print_r($selectedPermissions, true) }} <input id="permissionsListing" name="permissionsListing" value="add permissionsListing TEST"> <script src="https://cdn.jsdelivr.net/npm/virtual-select-plugin@1.0.39/dist/virtual-select.min.js" integrity="sha256-Gsn2XyJGdUeHy0r4gaP1mJy1JkLiIWY6g6hJhV5UrIw=" crossorigin="anonymous"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/virtual-select-plugin@1.0.39/dist/virtual-select.min.css" integrity="sha256-KqTuc/vUgQsb5EMyyxWf62qYinMUXDpWELyNx+cCUr0=" crossorigin="anonymous"> <div> AAAAA<div id="permissions"></div>BBB <script> function adminUsersPermissionsEditorComponent() { console.log('adminUsersPermissionsEditorComponent::') return { fillUsersPermissions: function () { var permissionsListing = @json($permissionsListing) VirtualSelect.init({ ele: '#permissions', multiple: true, options: permissionsListing, }); let selectedPermissions = document.querySelector('#permissions'); selectedPermissions.addEventListener('change', () => { let data = selectedPermissions.value; @this.set('selectedPermissions', data); }); }, onAdminUsersPermissionsEditorComponentInit: function () { }, } } </script> </div> </div>
问题原因及修复方案
1. Livewire DOM更新销毁Virtual Select实例
Livewire的自动DOM diff机制会在组件状态(如selectedPermissions)变更时重新渲染部分DOM,而Virtual Select是直接挂载在原生DOM节点上的,重新渲染后原实例丢失,导致组件消失。
修复:
用wire:ignore标记Virtual Select的容器,告诉Livewire不要修改该DOM节点:
<div wire:ignore id="permissions"></div>
2. 初始化方法未调用
当前代码中fillUsersPermissions方法未被执行,导致Virtual Select未正确初始化。
修复:
在Alpine的x-init中调用初始化方法:
x-init="fillUsersPermissions()"
3. 资源加载位置错误
将Virtual Select的JS和CSS放在组件内部会导致每次组件渲染重复加载,引发冲突。
修复:
把资源移到项目主布局文件(如layouts/app.blade.php)的对应位置:
<!-- 在<head>标签内加载CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/virtual-select-plugin@1.0.39/dist/virtual-select.min.css" integrity="sha256-KqTuc/vUgQsb5EMyyxWf62qYinMUXDpWELyNx+cCUr0=" crossorigin="anonymous"> <!-- 在</body>标签前加载JS --> <script src="https://cdn.jsdelivr.net/npm/virtual-select-plugin@1.0.39/dist/virtual-select.min.js" integrity="sha256-Gsn2XyJGdUeHy0r4gaP1mJy1JkLiIWY6g6hJhV5UrIw=" crossorigin="anonymous"></script>
4. 组件数据加载优化
避免在render方法中查询数据库,改为在mount钩子中加载,减少重复查询:
public function mount() { $this->permissionsListing = Permission::query() ->get() ->map(function ($permission) { return [ 'id' => $permission->id, 'label' => Str::replace('_', ' ', Str::title($permission->name)) ]; }) ->toArray(); } public function render() { return view('livewire.admin.users-permissions-editor'); }
5. 监听Livewire更新事件,重新初始化组件
为了避免Livewire更新DOM后组件失效,添加全局监听事件,在Livewire更新后重新初始化Virtual Select:
document.addEventListener('livewire:update', () => { if (window.virtualSelectInstance) { window.virtualSelectInstance.destroy(); } adminUsersPermissionsEditorComponent().fillUsersPermissions(); });
修改后的完整Blade视图
<div class="admin_page_container" id="users_permissions_editor_admin_page_container"> <div class="editor_listing_wrapper_bix_width" x-data="adminUsersPermissionsEditorComponent()" x-init="fillUsersPermissions()" x-cloak> <div>已选权限:{{ json_encode($selectedPermissions) }}</div> <div wire:ignore id="permissions"></div> <script> function adminUsersPermissionsEditorComponent() { return { fillUsersPermissions: function () { // 销毁已有实例,避免重复初始化 if (window.virtualSelectInstance) { window.virtualSelectInstance.destroy(); } const permissionsListing = @json($permissionsListing); window.virtualSelectInstance = VirtualSelect.init({ ele: '#permissions', multiple: true, options: permissionsListing, selectedValue: @json($selectedPermissions), // 设置初始选中项 }); // 监听选择变化,更新Livewire状态 document.querySelector('#permissions').addEventListener('change', (e) => { @this.set('selectedPermissions', e.target.value); }); }, } } // Livewire更新后重新初始化组件 document.addEventListener('livewire:update', () => { if (window.virtualSelectInstance) { window.virtualSelectInstance.destroy(); } adminUsersPermissionsEditorComponent().fillUsersPermissions(); }); </script> </div> </div>
内容的提问来源于stack exchange,提问作者Petro Gromovo
相关产品推荐
相关产品推荐

