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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 20:12:13