Введение
Поле ввода числа, как показано на рисунке ниже, представляет собой просто поле ввода с кнопками «плюс» и «минус». Оно в основном используется для добавления и уменьшения количества товаров в корзине. Этот компонент поля ввода не должен вызывать затруднений на первый взгляд. , но у конкретной реализации Element есть чему поучиться. , Читать исходный код действительно сложно! Код официального сайтакликните сюда
HTML-структура поля ввода числа
HTML-структура этого компонента относительно проста, на первый взгляд я бы подумал, что это div на внешнем слое, input на внутреннем слое и span слева и справа в виде кнопок.Посмотрев исходный код , это действительно так.Упрощенная структура html выглядит следующим образом
<div class='el-input-number'>
<span class="el-input-number__decrease"></span>
<span class="el-input-number__increase"></span>
<el-input></el-input>
</div>
Первые 2 пролета - кнопки плюс и минус, последний<el-input>
Это компонент поля ввода, инкапсулированный ранее. Обратите внимание, что это не собственный ввод. Здесь стоит упомянуть, что два интервала абсолютно позиционированы, и<el-input>
Левый и правый отступы равны 50px, как показано ниже.
Анализ отдельных частей
Первый взгляд на внешний div
<div
@dragstart.prevent
:class="[
'el-input-number',
inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
{ 'is-disabled': inputNumberDisabled },
{ 'is-without-controls': !controls },
{ 'is-controls-right': controlsAtRight }
]">
Первый ряд@dragstart.prevent
Когда я впервые увидел это, я был ошеломлен! Это предложение указывает на то, что поведение перетаскивания div по умолчанию запрещено.Здесь не очень понятно.Прежде всего, если div нужно перетаскивать, он должен быть установленdraggable="ture"
Это работает, а почему перетаскивание должно быть запрещено? Я пытался удалить это предложение, а затем перетащить компонент
draggable="ture"
Не удается перетащить выбранные числаЗатем классовая часть div
'el-input-number'
Базовый класс внешнего div указывается следующим образом
Видимый div устанавливается как встроенный элемент встроенного блока, а затем устанавливается ширина.Поскольку ширина компонента не изменится с изменением содержимого, ширина фиксирована, а следующие три класса управляют тем, будет ли компонент отключена (отключить логику. Предыдущие статьи были проанализированы), отображать ли кнопки плюс и минус, а также размещать ли кнопки с правой стороны, см. рисунок ниже
Добавляются ли эти три класса, понимается пользователем, передающим соответствующие реквизиты.Код scss на изображении выше, над которым я работал в течение длительного времени, — это кнопка минус в правом нижнем углу изображения выше.
@include when(controls-right) {
@include e(decrease) {
right: 1px;
bottom: 1px;
top: auto;
left: auto;
border-right: none;
border-left: $--border-base;
border-radius: 0 0 $--border-radius-base 0;
}
}
Это значит, когдаcontrols-right
После добавления классаdecrease
css этого класса изменен на вышеуказанное содержание, то есть кнопка минус ставится из исходного левого в правый нижний угол, я сначала не понимаю.top:auto,left:auto
Что это такое?Позже консольная отладка узнала, что, поскольку исходная вершина класса декады равна 1px, а левая — 1px, когдаcontrols-right
После того, как класс добавлен, для top и left необходимо установить значение auto, чтобы браузер мог автоматически вычислять top и left, иначе исходные top: 1px, left: 1px не могут быть перезаписаны. Еще стоит отметить, что высота кнопок «плюс» и «минус» здесь задается следующим образом.
height: auto;
line-height: #{($--input-height - 2) / 2};
Укажите высоту как auto и растяните высоту, установив значение высоты строки на половину высоты поля ввода минус ширина границы.Если вы прямо установите высоту на половину высоты, все должно быть в порядке, верно? ? Затем текст в поле ввода центрируетсяtext-align:center
выполнить
Далее рассмотрим логическую реализацию кнопок сложения и вычитания клавиш.Хтмл-код выглядит следующим образом.Эта кнопка реализована спаном,а не нативной кнопкой.
<span
class="el-input-number__decrease"
role="button"
v-if="controls"
v-repeat-click="decrease"
:class="{'is-disabled': minDisabled}"
@keydown.enter="decrease">
<i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
</span>
Роль атрибута роли состоит в том, чтобы сообщить приложениям специальных возможностей (таким как программы для чтения с экрана, удобные программы для слепых людей для доступа в Интернет), что роль, которую играет этот элемент, в основном предназначена для людей с ограниченными возможностями. Использование роли может улучшить читабельность и семантику текста, а затем элементы управления v-if являются логическим значением, которое является реквизитом, передаваемым пользователем для управления отображением кнопки, а затем:class
Определяет, отображает ли кнопка отключенный стиль,@keydown.enter
Это снова меня смутило, это прослушивание нажатия клавиши ввода. Соответствующие инструкции на официальном сайте Vue заключаются в том, чтобы добавить это событие к вводу. Когда ввод получает фокус, нажатие ввода вызовет соответствующее событие, но почему это должно быть добавлено в диапазон?@keydown.enter
, пробовал нажимать энтер и ничего не происходит, короче я тут не разбираюсь
Потом я обнаружил, что без этой кнопки нет события @click, а вся логика обработки кликов вынесенаv-repeat-click="decrease"
Внутри, помимо клика по операции увеличения или уменьшения числа, есть еще мышь, которая продолжает нажимать и удерживать, чтобы быстро увеличивать или уменьшать число.Вся логика реализована через пользовательские директивы в Vue.Обычно используются пользовательские директивы. Чтобы работать с базовым элементом dom, активируйте определенную логику. существуетdirectives
объявить в атрибуте
directives: {
repeatClick: RepeatClick
}
Этот ключ (repeatClick) соответствуетv-repeat-click
,value(RepeatClick) — это импортированный метод, см. код ниже
import { once, on } from 'element-ui/src/utils/dom';
export default {
bind(el, binding, vnode) {
let interval = null;
let startTime;
const handler = () => vnode.context[binding.expression].apply();
const clear = () => {
if (new Date() - startTime < 100) {
handler();
}
clearInterval(interval);
interval = null;
};
on(el, 'mousedown', (e) => {
if (e.button !== 0) return;
startTime = new Date();
once(document, 'mouseup', clear);
clearInterval(interval);
interval = setInterval(handler, 100);
});
}
};
Этот код немного сложнее. Прежде всего, вы должны быть знакомы с содержанием пользовательских инструкций Vue. Пользовательские инструкции будут предоставлять несколько функций ловушек для запуска определенной логики в определенное время, как показано на рисунке ниже.
используется здесьbind
Функцию ловушки можно понимать как однократный вызов инициализации.Вы думаете, что эта инструкция должна привязывать событие щелчка к элементу, поэтому вам нужно вызвать ее только один раз в привязке, а затемbind
Три параметраel,binding,vnode
Представляет действующий дом, объект привязки, который предоставляет различную информацию, и виртуальный узел, сгенерированный компиляцией Vue.
Объект привязки выглядит следующим образом
существуетbind
Логика этой функции-ловушки должна запускать метод сложения и вычитания чисел в поле ввода Этот метод написан в компоненте.methods
Внутри, то как получить этот метод, следующее предложение может получить
const handler = () => vnode.context[binding.expression].apply();
Я могу только сказать, что это предложение слишком высококлассное. Вы должны прочитать исходный код, чтобы написать его. Прежде всего, vnode — это виртуальный узел, сгенерированный vue, который является просто объектом js. В нем много атрибутов. это, тоcontext
Что это такое, посмотрите исходный код vue, чтобы знать, что структура vnode следующая
Component
Тип структуры данных, этот Компонент — это структура, определяемая потоком, вы можете увидеть содержимое в потоке в исходном коде vue, Компонент — это компонент, поэтому этот контекст — это контекст компонента, в котором находится vnode, а затем посмотри наbinding.expression
, официальный сайт говорит, что этоv-repeat-click="decrease"
Метод уменьшения в , этот метод прописан в методах компонента, затемcontext[binding.expression]
то естьcontext['decrease']
Итак, я получил метод удаления в компоненте, который аналогичен его использованию в компоненте.this.decrease
то же самое, то последнееapply()
Это очень странно. Использование apply заключается в том, что первый параметр параметра указывает целевой объект для выполнения. Если он равен нулю или не определен, это означает, что метод вызывается в окне. Здесь нет параметра, то есть undefined, поэтому оно выполняется в окне. Я не уверен, правильно это или нет, поэтому я изменил это предложение на
const handler = () => vnode.context[binding.expression].apply(vnode);
Ошибки нет, и я не знаю, почему можно применить() напрямую.Я изменю вышесказанное на следующее, то есть выполнить функцию напрямую, об ошибке не сообщается, все в норме
const handler = () => vnode.context[binding.expression]()
назадbind
Логика метода обнаружила, что нет никакогоclick
Похоже, что события щелчка мыши не связаны, нажмите здесь, потому что вы хотите иметь дело с методом триггера непрерывного уменьшения, поэтому непрерывное нажатие и щелчок смешивают оба вместе, как показано ниже.
on(el, 'mousedown', (e) => {
if (e.button !== 0) return;
startTime = new Date();
once(document, 'mouseup', clear);
clearInterval(interval);
interval = setInterval(handler, 100);
});
on
Этот метод находится вне каталога исходного кода.Поскольку можно использовать и другие компоненты, он извлекается в общедоступный метод и помещается в каталог util. Первый взглядon
код
export const on = (function() {
if (!isServer && document.addEventListener) {
return function(element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
};
} else {
return function(element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
};
}
})();
Этот метод предназначен для привязки событий к элементам, а if-else обрабатывает ситуацию совместимости,attachEvent
это метод, т.е.addEventListener
это метод других основных браузеров.on
Третий параметр — это функция обработчика событий,on
в первом предложенииif (e.button !== 0) return
изe.button
какая кнопка мыши была нажата
onclick
реагирует только на нажатие левой кнопки мыши, аonmousedown
Потом реагирует на нажатие 3-х клавиш, так что тут надо различать.
on
Последнее предложениеinterval = setInterval(handler, 100)
Таймер настроен на регулярное выполнение метода обработчика, чтобы вызвать событие увеличения или уменьшения числа каждые 0,1 с.Затем мы думаем, что при нажатии мыши событие добавляется к элементу dom: обработчик выполняется регулярно, тогда он должен будет уничтожен, когда мышь поднята.Таймер, в противном случае метод обработчика будет запускаться бесконечно, заставляя число все время увеличиваться или уменьшаться, поэтомуonce(document, 'mouseup', clear)
Это предложение, чтобы уничтожить таймер, когда мышь поднята, сначала посмотрите на метод очистки
const clear = () => {
if (new Date() - startTime < 100) {
handler();
}
clearInterval(interval);
interval = null;
};
Внутри есть clearInterval, чтобы уничтожить таймер. Предыдущая логика if очень важна. Запишите время, когда мышь нажата, и определите текущее время, когда мышь поднята - время нажатия once(document, 'mouseup', clear),once
является функцией высшего порядка, которая запускается только один раз, код выглядит следующим образом
export const once = function(el, event, fn) {
var listener = function() {
if (fn) {
fn.apply(this, arguments);
}
off(el, event, listener);
};
on(el, event, listener);
};
Это способ записи один раз в режиме наблюдателя, который, по сути, мультиплексирует событие on, но изменяется третий параметр on, fn будет выполняться один раз в прослушивателе, а затем метод off будет использоваться для удаления прослушивателя. , тем самым достигая цели выполнения только один раз.Еще один момент, который следует отметить, это то, что первым параметром метода Once является документ, который также очень важен.Вы можете подумать, что если вы привязываете onmousedown к кнопкам «плюс» и «минус», вы должны привязать onmouseup к кнопкам «плюс» и «минус». вызовет ошибки. Рассмотрим ситуацию, когда вы нажимаете мышью на кнопки «плюс» и «минус», затем перемещаете мышь за пределы кнопки, а затем отпускаете мышь, вы обнаружите, что в это время число все еще увеличивается, это ошибка, поэтому она должна быть в самой внешней части документа. Привяжите mouseup к элементу dom слоя, чтобы всегда можно было отреагировать на событие mouseup, иначе перемещение мыши приведет к постоянному увеличению числа.
Разбор не двигается, основные сложности написаны, остальные атрибуты точности и шаги не сложны, короче сложно понять весь код, сосредоточьтесь только на части основной логики