Недавно я увидел две очень хорошие статьи в Nuggets:
- Реализуйте дросселирование или устранение дребезга в виде компонентов или плагинов vue.
- Трюки и хитрости - Vue Mixins Advanced Components и Vue HOC Advanced Components Practice
В обеих статьях авторы делятся своим опытом включения функции подавления дребезга/регулирования функции в универсальные компоненты.
Я не буду вводить здесь концепцию функции анти-шейк/троттлинг функции, действительно очень практично инкапсулировать такие функции в виде компонентов.
Еще мне нравится идея инкапсуляции через HOC (компоненты более высокого порядка), и я тоже хочу поделиться здесь подобным методом инкапсуляции.
абстрактный компонент
Здесь я использовалabstract: trueдля создания абстрактного компонента.
наш обычныйtransitionа такжеkeep-aliveявляется абстрактным компонентом. Абстрактный компонент не имеет состояния, а также «не существует», он не отображается в самом фактическом DOM, а напрямую возвращает и манипулирует своими дочерними элементами.
Например для шаблона (Debounceявляется абстрактным компонентом):
<Debounce>
<button>123</button>
</Debounce>
будет отображаться как:
<button>123</button>
выполнить
Код компонента размещен непосредственно здесь:
const debounce = (func, time, ctx) => {
let timer
const rtn = (...params) => {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(ctx, params)
}, time)
}
return rtn
}
Vue.component('Debounce', {
abstract: true,
props: ['time', 'events'],
created () {
this.eventKeys = this.events.split(',')
this.originMap = {}
this.debouncedMap = {}
},
render() {
const vnode = this.$slots.default[0]
this.eventKeys.forEach((key) => {
const target = vnode.data.on[key]
if (target === this.originMap[key] && this.debouncedMap[key]) {
vnode.data.on[key] = this.debouncedMap[key]
} else if (target) {
this.originMap[key] = target
this.debouncedMap[key] = debounce(target, this.time, vnode)
vnode.data.on[key] = this.debouncedMap[key]
}
})
return vnode
},
})
Debounceкомпонент приметtimeа такжеevents(разделенные запятыми) два аргумента.
существуетrenderв функции,DebounceКомпонент модифицирует событие дочернего VNode и возвращает его обратно.
использовать
Тогда давайте использовать его:
<div id="app">
<Debounce :time="1000" events="click">
<button @click="onClick($event, 1)">click+1 {{val}}</button>
</Debounce>
<Debounce :time="1000" events="click">
<button @click="onClick($event, 2)">click+2 {{val}}</button>
</Debounce>
<Debounce :time="1000" events="mouseup">
<button @mouseup="onAdd">click+3 {{val}}</button>
</Debounce>
<Debounce :time="1000" events="click">
<button @mouseup="onAdd">click+3 {{val}}</button>
</Debounce>
</div>
const app = new Vue({
el: '#app',
data () {
return {
val: 0,
}
},
methods: {
onClick ($ev, val) {
this.val += val
},
onAdd () {
this.val += 3
}
}
})
инструкции по использованию
Использование пользовательской команды также является идеей, но привязка команды происходит вcreatedВ обратном вызове события, то есть после инициализации события, чтобы его нельзя было модифицироватьvnode.data.onЧтобы изменить обратный вызов связанного события, вы можете только привязать событие самостоятельно:
Vue.directive('debounce', {
bind (el, { value }, vnode) {
const [target, time] = value
const debounced = debounce(target, time, vnode)
el.addEventListener('click', debounced)
el._debounced = debounced
},
destroy (el) {
el.removeEventListener('click', el._debounced)
}
})
Здесь следует отметить, что командаbinding.valueПроцесс оценки и привязка событий отличаются и не поддерживаютonClick($event, 2)написано, поэтому если такая привязка может быть обернута только еще одним слоем:
<button v-debounce="[($ev) => { onClick($ev, 4) }, 500]">click+4 {{val}}</button>
резюме
Преимущество использования абстрактных компонентов заключается в повышении универсальности компонентов, он не загрязняет DOM из-за использования компонентов (добавление нежелательных тегов div и т. д.) и может обернуть любой отдельный дочерний элемент. также недостатки, такие как внимание при его использовании.Дочерние элементы могут содержать только один корень, что довольно многословно для использования (см. статью вButtonHocОн более лаконичен в использовании, но соответственно может использоваться только какButtonрендеринг).