ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Select2 默认 DataAdapter(SelectAdapter)完全指南:数据适配器、装饰器与 v3.5 兼容层

Select2 默认 DataAdapter(SelectAdapter)完全指南:数据适配器、装饰器与 v3.5 兼容层 Select2 默认 DataAdapterSelectAdapter完全指南数据适配器、装饰器与 v3.5 兼容层【免费下载链接】select2Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.项目地址: https://gitcode.com/gh_mirrors/se/select2导读本文围绕 Select2 数据层的默认实现SelectAdapter展开它继承自BaseAdapter负责把select元素中的option/optgroup转换为 Select2 内部数据对象并完成查询、选中、取消选中等核心行为。你会了解 Select2 如何通过dataAdapter配置项替换数据源、六大装饰器Tags、MinimumInputLength、MaximumInputLength、InitSelection、Query、InputData各自的作用与底层实现以及 v3.5 时代遗留 APIinitSelection、query、input typehidden是如何被兼容的——读完可直接定位对应源码、按需覆写数据适配器。一、SelectAdapter默认 DataAdapter 的实现与定位Select2 将「数据从哪来、如何查询、如何增删选中项」这类职责抽象为DataAdapter。官方文档明确指出Select2 提供SelectAdapter作为DataAdapter的默认实现它继承自BaseAdapter。1.1 如何覆写默认数据适配器你可以通过配置选项dataAdapter传入自定义适配器完全替换默认的数据行为$(#mySelect).select2({ dataAdapter: MyCustomDataAdapter });在初始化时dataAdapter是 Select2 判断数据来源的“总开关”。这一点在 src/js/select2/defaults.js 的Defaults.prototype.apply中有清晰的优先级逻辑if (options.dataAdapter null) { if (options.ajax ! null) { options.dataAdapter AjaxData; // 远程 AJAX 数据 } else if (options.data ! null) { options.dataAdapter ArrayData; // JS 数组数据 } else { options.dataAdapter SelectData; // 默认select 元素 } // ... 随后按配置依次叠加装饰器 }可见只要用户没有显式指定dataAdapterSelect2 就按ajax→data→ 默认SelectData的顺序自动挑选基础适配器而SelectAdapter源码模块select2/data/select正是兜底的默认实现。1.2 两个 AMD 模块官方文档列出的相关 AMD 模块为select2/data/base—— 即 src/js/select2/data/base.js定义BaseAdapterselect2/data/select—— 即 src/js/select2/data/select.js定义SelectAdapter。二、BaseAdapter 抽象基类五个核心方法BaseAdapter是所有数据适配器的抽象基类它通过Utils.Extend(BaseAdapter, Utils.Observable)src/js/select2/data/base.js继承了事件观察能力。从源码看它定义了子类必须或可选实现的五个方法方法作用基类默认行为current(callback)返回当前选中的数据直接throw要求子类必须实现query(params, callback)按搜索参数查询结果集直接throw要求子类必须实现bind(container, $container)绑定容器事件空实现子类可覆写destroy()清理资源空实现子类可覆写generateResultId(container, data)为结果项生成唯一 DOM id基类提供通用实现其中current和query在基类中直接抛错src/js/select2/data/base.jsBaseAdapter.prototype.current function (callback) { throw new Error(The current method must be defined in child classes.); }; BaseAdapter.prototype.query function (params, callback) { throw new Error(The query method must be defined in child classes.); };这一点在测试 tests/data/base-tests.js 中被显式验证current is required与query is required两个 QUnit 用例断言调用未实现的current/query会抛错说明任何数据适配器都必须自行实现这两个方法。generateResultId则是基类给出的通用工具方法它组合容器 id、随机字符与数据 id 生成形如容器id-result-xxxx-1的稳定结果标识src/js/select2/data/base.js后续下拉列表与无障碍a11y功能都依赖该 id。三、SelectAdapter 的实现细节从 DOM 到数据对象SelectAdaptersrc/js/select2/data/select.js把select元素当作数据源其关键方法包括3.1 current()读取当前选中项current通过this.$element[0].querySelectorAll(:checked)找到所有处于选中状态的option再逐个调用item()转为内部数据对象后回调src/js/select2/data/select.js。这正是文档所说「current方法取代 v3.5 的initSelection回调」的落点。3.2 query()遍历子元素完成本地匹配query遍历select的所有子节点跳过非option/optgroup的节点调用item()转数据对象后交给matches()判断是否命中最后以{ results: data }形式回调src/js/select2/data/select.jsSelectAdapter.prototype.matches function (params, data) { var matcher this.options.get(matcher); return matcher(params, data); };matches直接读取配置项matchersrc/js/select2/data/select.js。matcher的默认实现位于 src/js/select2/defaults.js它执行「先比对子项children再对文本做去重音diacritics后大小写不敏感的子串匹配」。这也印证了官方文档中“matcher只对本地数据生效”的警告——远程数据应在服务端完成过滤。3.3 select() / unselect()同步 DOM 选中状态select(data)将数据标记selected true。若数据对应真实option节点直接设置option.selected true否则在单选中写入data.id多选中合并去重后写入$element.val(...)最后统一触发input与change事件src/js/select2/data/select.js。unselect(data)仅对多选生效单选直接返回移除对应 id 后同样触发input/changesrc/js/select2/data/select.js。3.4 option() / item()DOM 节点与数据对象的双向转换这是SelectAdapter的核心枢纽option(data)把内部数据对象还原为 DOM 节点——含children时创建optgroup以data.text作为 label否则创建option并把id→value、disabled、selected、title等属性写回 DOMsrc/js/select2/data/select.js。item($option)把 DOM 节点转为数据对象。若是option提取id、text、disabled、selected、title若是optgroup则递归处理其子option组装出children数组src/js/select2/data/select.js。_normalizeItem(item)统一数据形状——字符串会被包装成{ id: item, text: item }强制补齐selected: false、disabled: false默认值id/text 统一转字符串并为带 id 的项生成_resultId同时递归规范化childrensrc/js/select2/data/select.js。3.5 bind() / destroy()bind订阅容器上的select/unselect事件分别转发到select()与unselect()src/js/select2/data/select.jsdestroy则清理子元素上由 Select2 写入的自定义数据src/js/select2/data/select.js。四、六大装饰器Decorators逐一解析Select2 的数据层大量采用装饰器模式Defaults.apply会按配置项用Utils.Decorate把装饰器逐层包在基础适配器外层src/js/select2/defaults.jsUtils.Decorate的实现位于 src/js/select2/utils.js 附近。装饰器通过decorated参数即被包装类构造器或上一个被包装方法调用原始实现。4.1Tagsselect2/data/tags实现 tagging标签功能select multiplemultiple option selectedselectedorange/option optionwhite/option option selectedselectedpurple/option /select$(.js-example-tags).select2({ tags: true });底层实现见 src/js/select2/data/tags.js构造时若tags为数组会把数组元素逐项_normalizeItem后追加为optionsrc/js/select2/data/tags.jsquery被包装若搜索词没有命中任何现有选项就调用createTag生成新标签、以data-select2-tag属性标记并插入结果集src/js/select2/data/tags.js默认createTag用去掉首尾空白的搜索词生成{ id: term, text: term }空词返回nullinsertTag默认把新标签unshift到结果集最前src/js/select2/data/tags.js_removeOldTags会清理未被选中的历史临时标签src/js/select2/data/tags.js。4.2MinimumInputLengthselect2/data/minimumInputLength实现 最小搜索词长度minimumInputLength$(.js-example-basic-single).select2({ minimumInputLength: 2 });源码见 src/js/select2/data/minimumInputLength.js包装后的query先取params.term空则置为若其长度小于minimumInputLength就触发results:message事件并携带{ message: inputTooShort, args: { minimum, input, params } }不再调用被装饰的query只有长度达标才继续委托给原查询逻辑。注意minimumInputLength的默认值是0src/js/select2/defaults.js且只有 0时才会被装饰src/js/select2/defaults.js。4.3MaximumInputLengthselect2/data/maximumInputLength实现 最大搜索词长度maximumInputLength$(.js-example-basic-single).select2({ maximumInputLength: 20 });源码见 src/js/select2/data/maximumInputLength.js逻辑与上一装饰器对称——当maximumInputLength 0且词长超限时触发results:message事件message: inputTooLong并短路返回不执行原query。4.4InitSelectionselect2/compat/initSelection这是为Select2 3.5 及更早版本提供的向后兼容装饰器文档中列出的 AMD 模块写法为select2/compat/initSelection。背景旧版 Select2 在使用自定义数据源时必须提供名为initSelection的配置回调用它来决定组件初始选中值。从 4.x 开始这一职责被数据适配器的current方法彻底取代——如本文 3.1 节所示SelectAdapter.current会从 DOM 中读取已选中项并转换为数据对象。因此迁移到 4.x 后不再需要initSelection回调初始选中逻辑统一由current()负责旧回调可通过该装饰器获得兼容支持但新代码应直接实现数据适配器的current。4.5Queryselect2/compat/query同样是为 3.5 提供向后兼容的装饰器AMD 模块为select2/compat/query。背景旧版 Select2 允许通过query配置回调完全接管结果查询例如向服务端发起搜索。4.x 的等价物是数据适配器自身的query方法见 3.2 节的SelectAdapter.query以及必须由子类实现的 src/js/select2/data/base.js。Query装饰器负责把旧式的query回调接入新版数据适配器调用链让老代码无需重写即可运行新项目则应通过自定义dataAdapter并实现query来达成同样目的。4.6InputDataselect2/compat/inputData这是对 3.5 时代input typehidden元素的兼容装饰器AMD 模块为select2/compat/inputData。背景旧版 Select2 中select元素只能配合有限的一部分配置使用很多场景必须改用input typehidden承载数据——这导致未开启 JavaScript 的访客无法优雅降级。从 4.x 起Select2 的所有选项均支持select元素因此不再需要再使用input /元素。InputData装饰器仅用于读取旧版本遗留的 hidden input 写法让历史项目平滑升级。五、装饰器如何被装配从配置到调用链综合 src/js/select2/defaults.js当未显式设置dataAdapter时Select2 会按以下顺序为数据适配器叠加装饰器// 1. 选定基础适配器AjaxData / ArrayData / SelectData // 2. minimumInputLength 0 → 包一层 MinimumInputLength // 3. maximumInputLength 0 → 包一层 MaximumInputLength // 4. maximumSelectionLength 0 → 包一层 MaximumSelectionLength // 5. tags 为真 → 包一层 Tags // 6. tokenSeparators / tokenizer → 包一层 Tokenizerif (options.minimumInputLength 0) { options.dataAdapter Utils.Decorate(options.dataAdapter, MinimumInputLength); } if (options.maximumInputLength 0) { options.dataAdapter Utils.Decorate(options.dataAdapter, MaximumInputLength); } if (options.tags) { options.dataAdapter Utils.Decorate(options.dataAdapter, Tags); }理解这条装配链对排查问题很有价值顺序即优先级每一层装饰器都包裹在前一层之外先被装饰的行为会先被触发。例如MinimumInputLength在Tags外层意味着搜索词过短时标签创建逻辑Tags.query根本不会执行dataAdapter被显式设置后整条自动装配链会被跳过if (options.dataAdapter null)整块不执行自定义适配器需自行决定是否复刻这些装饰行为三种输入长度/标签相关的装饰器同时作用于query而maximumSelectionLength装饰器模块select2/data/maximumSelectionLength默认值0见 src/js/select2/defaults.js则主要约束多选场景的选中数量与搜索输入无关。六、自定义 DataAdapter 的实战建议综合以上源码事实编写自定义数据适配器时应遵循继承BaseAdapter模块select2/data/base并用Utils.Extend(MyAdapter, BaseAdapter)建立继承关系必须实现current(callback)与query(params, callback)否则会直接抛错src/js/select2/data/base.js、tests/data/base-tests.js通过bind(container, $container)订阅select/unselect事件并在destroy()中清理副作用参考SelectAdapter.bind/destroysrc/js/select2/data/select.js复用基类的generateResultId保证结果 DOM id 的一致性src/js/select2/data/base.js若需要保留输入长度限制、标签等内置能力在自定义dataAdapter中自行用Utils.Decorate组合对应装饰器模块。总结SelectAdapter是 Select2 数据层的默认实现与自定义适配器的模板BaseAdapter用current/query两个必须方法定义了数据源契约SelectAdapter则给出「selectDOM ⇄ 内部数据对象」的完整双向转换与查询实现。围绕它Tags、MinimumInputLength、MaximumInputLength三个装饰器提供了开箱即用的常用能力InitSelection、Query、InputData三个兼容装饰器则为 3.5 时代遗留代码铺平了升级之路。无论你是想替换数据源、约束搜索输入还是迁移老项目都可以从dataAdapter配置入手沿着本文给出的源码路径src/js/select2/data/、src/js/select2/defaults.js、tests/data/快速定位并验证行为。【免费下载链接】select2Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.项目地址: https://gitcode.com/gh_mirrors/se/select2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表