предисловие
Эта статья начинается со структуры кода компонента select, рассказывается, как компонент select организует связь между родительским и дочерним компонентами, и фокусируется на гениальной реализации компонентов ElSelect и ElOption. И познакомьтесь с тем, как реализованы четыре функции компонента select.
структура кода
Элемент использует div для имитации выбора, а компонент выбора содержитnavigation-mixin.js
,option-group.vue
,option.vue
,select-dropdown.vue
,select.vue
и т.д. документы
Этоselect.vue
Структура HTML основного файла аналогична структуре основного аналога select, но Element немного сложнее.Структура HTML компонента select выглядит следующим образом (для ясности некоторые коды опущены):
<div class="el-select">
<!-- 多选的情况 -->
<div v-if="multiple" class="el-select__tags">
<span>
<!-- 放置多选时的选中的tag,以tag展现,或者合并成一段文字 -->
</span>
<!-- 搜索功能 -->
<input v-model="query" v-if="filterable" />
</div>
<!-- 单选的时候选中的值回显 -->
<el-input v-model="selectedLabel" :class="{ 'is-focus': visible }">
<!-- xxx -->
</el-input>
<!--下拉框-->
<el-select-menu>
<el-scrollbar v-show="options.length > 0 && !loading">
<!-- 选项内容 -->
<el-option :value="query" created v-if="showNewOption"> </el-option>
<slot></slot>
</el-scrollbar>
<!-- options为空显示的默认文字或者select处于loading的情况 -->
<template
v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0 ))"
>
<!-- xxx -->
</template>
</el-select-menu>
</div>
Как организовать родительские и дочерние компоненты
Анализируя структуру кода в предыдущем разделе, читатели могут подумать, что компонент select очень сложен.Как он обрабатывает связь между родительским и дочерним компонентами? Прежде чем я проанализировал метод организации компонента «Таблица», я использовал простойрежим хранения, компонент select использует широковещательную рассылку/отправку и внедрение/предоставление. (PS: трансляцию пишет ElementUI, vue 2.0 удален$dispatch 和 $broadcast
)
вводить и обеспечивать
Vue2.2.0 добавляет API, эту пару опций необходимо использовать вместе, чтобы позволить компоненту-предку внедрить зависимость во всех своих потомков, независимо от того, насколько глубоким является уровень компонента, и это всегда будет действовать, когда восходящий и нисходящий потоки отношения устанавливаются. В двух словах: предоставьте переменные в компонентах-предках через провайдера и вставьте переменные в компоненты-потомки через inject.
Позже я разберу, как ElementUI использует inject и provider, и почему их нельзя использовать.this.$parent
а такжеthis.$children
общаться друг с другом
рассылка и рассылка
Код трансляции помещается в 'element-ui/src/mixins/emitter', микшируется через миксины, например:this.broadcast('ElOption', 'queryChange', '');
а такжеthis.dispatch("ElSelect", "setSelected");
Как фронтенд, который работает в индустрии всего больше года, я никогда не использовал вещание.Vue$dispatch 和 $broadcast
Детальное объяснение
Хотя широковещательная передача имеет свои недостатки, например, метод потока событий, основанный на древовидной структуре компонентов, действительно сложен для понимания и не решает проблему связи между родственными компонентами. Но во вложенных компонентах родитель-потомок,$dispatch 和 $broadcast
Прямой удаленный вызов событий родительскому или дочернему компоненту, избегая операции вызова методов экземпляра компонента путем передачи свойств или использования ссылок, по-прежнему очень лаконичен.
ElSelect и ElOption
Если я напишу этот компонент, его можно будет использовать так<my-select v-model="input" options="options" />
, то есть опции компонента Select передаются в компонент через options . Возможно, окончательная функция может быть достигнута, но для этого она кажется слишком инкапсулированной. Из структуры этого компонента и нативногоselectНе совсем. Мы часто используем компонент Select таким образом, не правда ли, он интуитивно понятен:
<el-select v-model="input" filterable clearable placeholder="请选择">
<el-option label="foo" value="foo"></el-option>
<el-option label="foo" value="foo"></el-option>
</el-select>
Из структуры кода мы также можем видеть, что в компоненте выбора есть слот по умолчанию, и нет проблем с отображением, но, учитывая, что компоненту выбора необходимо взаимодействовать с компонентом выбора, например, компоненту выбора требуется чтобы узнать, есть ли у компонента выбора несколько вариантов выбора, можно ли его искать и т. д. Компонент выбора должен знать количество компонентов параметров и т. д. Как это сделать? Ответ ввести и предоставить
Непосредственно поместите свой собственный экземпляр в выбранный компонентthis
Введенный в компонент опции, компонент опции проходитthis.select
Измените свойства родительского компонента напрямую:
provide() {
return {
'select': this
};
}
Создайте столько вариантов, сколько у вас естьel-option
пример, посмотриel-option
изcreated
Жизненный цикл:
created() {
//把 el-option实例push进父组件select的options
this.select.options.push(this);
this.select.cachedOptions.push(this);
this.select.optionsCount++;
this.select.filteredOptionsCount++;
// 监听自定义的事件
this.$on('queryChange', this.queryChange);
this.$on('handleGroupDisabled', this.handleGroupDisabled);
}
Некоторые студенты могут спросить,this.select.options.push(this);
Почему это всегда операция push, если мои варианты неопределенны,select.options
Разве это не большая ценность. не волнуйся, посмотриel-option
изbeforeDestroy
Жизненный цикл
beforeDestroy() {
this.select.onOptionDestroy(this.select.options.indexOf(this));
}
выбор родительского компонентаonOptionDestroy
метод,el-option
Уничтоженный, он также будет удален из опций родительского компонента:
onOptionDestroy(index) {
if (index > -1) {
this.optionsCount--;
this.filteredOptionsCount--;
// cachedOptions 没有去除
this.options.splice(index, 1);
}
}
Функция
групповое отображение альтернатив
option-group.vue
Структура его HTML очень проста:
<ul class="el-select-group__wrap" v-show="visible">
<li class="el-select-group__title">{{ label }}</li>
<li>
<ul class="el-select-group">
<slot></slot>
</ul>
</li>
</ul>
Функция группировки эквивалентна обертыванию el-option в один слой, тогда на этот разel-option
компонент эквивалентенel-select
Внук компонента, то компоненты el-option и el-select не могут пройтиthis.$parent
связь, для совместимостиel-option
Для двух случаев дочернего компонента или внучатого компонента используйтеinject
а такжеprovide
просто подходит.
Локальный поиск
По сути, это локальная функция фильтрации, поэтому для включения функции локального поиска нужно пройтиfilterable
поле вместоsearchable
, функция фильтрации действительно хороша, как я уже говорил ранее, вы можете создать столько вариантов, сколько захотитеel-option
Экземпляры, поэтому, когда мы включаем фильтрацию, нам нужно только скрыть экземпляры el-option, которые не соответствуют обычному шаблону. Код выглядит следующим образом (он проще, чем ожидалось):
queryChange(query) {
// 因为select还可以手动创建条目,所以手动创建的条目一定显示
this.visible =
new RegExp(escapeRegexpString(query), 'i').test(this.currentLabel) ||
this.created;
if (!this.visible) {
// 筛选的选项数目减一
this.select.filteredOptionsCount--;
}
}
удаленный поиск
Часть кода настолько проста, потому что мы знаем удаленный поиск, все параметры возвращаются сервером и создаются заново.el-option
компоненты.
if (this.remote && typeof this.remoteMethod === 'function') {
this.hoverIndex = -1;
this.remoteMethod(val);
}
}
выберите эхо компонента удаленного поиска
element-ui Когда ваш параметр исправлен, он будет отображать соответствующую метку на основе выбранного вами значения, но компонент удаленного поиска связан сoptions
Не исправлено, проблема с эхом.
Решение состоит в том, чтобы передать выбранное значениеoptions
Входящий, скажем, у меня есть компонентArticleSelect
, значение идентификатора, которое я выбрал, равно [ 1,2 ], если оно не обработано, этот компонент не будет отображаться. Сухо отображаются только два тега 1 и 2. Но я могу получить выбранное значение, поставивoptions
(например, значение[{value:1,label:'第一篇'},{value:2,label:'第二篇'}]
), переданный в этот компонент, для достижения эхо-заголовка.
Однако некоторые люди могут спросить, не будут ли параметры удаленного поиска компонента select динамически изменяться с ключевыми словами поиска, почему это возможно? Давайте посмотрим на код компонента выбора ElementUI для установки выбранного значения:
setSelected() {
// 省略不是多选的情况的代码
// 多选
let result = [];
if (Array.isArray(this.value)) {
this.value.forEach(value => {
// 注意到这里是push操作,且getOption是从cachedOptions里面取的,(cachedOptions是被缓存的,不会因为el-option销毁而销毁)
result.push(this.getOption(value));
});
}
this.selected = result;
// 设置完成之后重新计算选项框的高度
this.$nextTick(() => {
this.resetInputHeight();
});
}
Из кода видно, что выбранное значение настройки Элемента является операцией push, поэтому последующие изменения опций не повлияют на выбранное мной значение, которое отлично решает мои потребности.
Создать запись
Вот поумнее, покажу структуру кода выпадающего окна:
<el-scrollbar v-show="options.length > 0 && !loading">
<!-- 选项内容 -->
<!-- 看到这个受 showNewOption 控制的option了嘛,他就是用来创建option -->
<el-option :value="query" created v-if="showNewOption"> </el-option>
<slot></slot>
</el-scrollbar>
вычисляемое свойствоshowNewOption
код:
showNewOption() {
let hasExistingOption = this.options.filter(option => !option.created)
.some(option => option.currentLabel === this.query);
// this.query为空的时候,因为v-if是真正的条件渲染,所以这个el-option组件又被销毁了,(没有手动去销毁哦,是不是很巧妙)
return this.filterable && this.allowCreate && this.query !== '' && !hasExistingOption;
}
Суммировать
Компонент select в Element немного сложен, но реализация функции, особенно идея структуры кода, действительно сложна. Что касается части CSS, я не буду разбирать ее, если это не моя сила.