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

如何将Vue2实现的自动补全搜索功能迁移为纯Web Components

Web Components自动补全搜索功能问题修复方案

核心问题梳理

  • 插槽内容未添加对应slot属性,无法匹配命名插槽,导致slot内容不渲染
  • slot仅作为内容占位节点,原生不支持直接绑定传递函数、复杂类型数据,原有slot传参逻辑完全不生效
  • 模板字符串定义在类外部,this指向全局window,无法读取组件实例的属性和方法
  • options属性的getter直接返回字符串类型的attribute值,调用数组方法filter会直接报错,复杂类型数据不能存在attribute中
  • 内联事件绑定(onchange/onfocus)仅能调用全局作用域函数,类方法序列化后无法正常触发,且原生Web Components不支持Vue的v-if指令
  • 父子组件无正常通信逻辑,数据、事件无法在search-select和auto-complete间同步

修复后完整代码

Search-select 组件

class SearchSelect extends HTMLElement {
    constructor() {
        super();
        this.query = "";
        this.results = [];
        // 内部存储options数组,不挂载到仅支持字符串的attribute上
        this._options = [
            "头脑特工队",
            "疾速追杀",
            "侏罗纪世界",
            "指环王",
            "环太平洋",
            "加勒比海盗",
            "猩球崛起",
            "电锯惊魂",
            "边境杀手",
            "僵尸世界大战"
        ];
        this.shadow = this.attachShadow({ mode: "open" });
    }

    setQuery(event) {
        this.query = event.target.value;
        this.results = this.filterMethod(this._options, this.query);
        // 触发自定义事件穿透shadow dom传递结果给子组件
        this.dispatchEvent(new CustomEvent('results-update', {
            detail: this.results,
            bubbles: true,
            composed: true
        }));
        this.render();
    }

    get options() {
        return this._options;
    }

    set options(val) {
        if(Array.isArray(val)) {
            this._options = val;
            this.results = this.filterMethod(this._options, this.query);
            this.render();
        }
    }

    static get observedAttributes() {
        return ["filterMethod"];
    }

    filterMethod(options, query) {
        return options.filter((option) =>
            option.toLowerCase().includes(query.toLowerCase())
        );
    }

    attributeChangedCallback(prop, oldValue, newValue) {
        if (prop === "filterMethod" && typeof window[newValue] === 'function') {
            this.filterMethod = window[newValue];
            this.results = this.filterMethod(this._options, this.query);
            this.render();
        }
    }

    render() {
        // 模板放在render方法内,this指向当前组件实例
        this.shadow.innerHTML = `
            <div>
                <slot name="autocomp"></slot>
            </div>
        `;
    }

    connectedCallback() {
        this.render();
    }
}

customElements.define("search-select", SearchSelect);

Autocomplete 组件

class Autocomplete extends HTMLElement {
    constructor() {
        super();
        this.dropdownVisible = false;
        this.results = [];
        this.shadow = this.attachShadow({ mode: "open" });
    }

    showDropdown() {
        this.dropdownVisible = true;
        this.render();
    }

    hideDropdown() {
        this.dropdownVisible = false;
        this.render();
    }

    render() {
        // 动态拼接结果列表,替代v-if指令
        const resultList = this.results.map(item => `<li>${item}</li>`).join('');
        this.shadow.innerHTML = `
            <search-select>
                <div class="autocomplete" slot="autocomp">
                    <input
                        type="text"
                        placeholder="输入内容搜索列表"
                    />
                    <div class="autocomplete-dropdown" style="display: ${this.dropdownVisible ? 'block' : 'none'}">
                        <ul class="autocomplete-search-results-list">
                            ${resultList}
                        </ul>
                    </div>
                </div>
            </search-select>
        `;
        // 手动绑定事件替代内联绑定
        const input = this.shadow.querySelector('input');
        const searchSelect = this.shadow.querySelector('search-select');
        input.addEventListener('input', (e) => searchSelect.setQuery(e));
        input.addEventListener('focus', () => this.showDropdown());
        // 加延迟避免点击下拉项时下拉框先隐藏导致点击失效
        input.addEventListener('blur', () => setTimeout(() => this.hideDropdown(), 200));
        // 监听父组件的结果更新事件同步数据
        searchSelect.addEventListener('results-update', (e) => {
            this.results = e.detail;
            this.render();
        });
    }

    connectedCallback() {
        this.render();
    }
}

customElements.define("auto-complete", Autocomplete);

页面使用方式

直接在HTML中插入组件标签即可调用:

<auto-complete></auto-complete>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:06:03