предисловие
Учащиеся, имеющие опыт использования echarts, возможно, сталкивались с таким сценарием. Метод echartsBox.resize() запускается в обратном вызове события window.onresize для достижения цели перерисовки. Событие resize запускается постоянно, что означает, что экземпляр echarts будет постоянно перерисовываться.Рисование очень требовательно к производительности. Существует также распространенный сценарий, в котором внутренний интерфейс запрашивается в событии ввода входного тега. Событие ввода также запускается постоянно. Если я введу «12», я дважды запрошу параметры интерфейса, которые являются " 1" и "12" соответственно. Что еще хуже, так это пустая трата сетевых ресурсов. Если запрос с параметром "1" возвращает данные позже, чем интерфейс с параметром "12", то данные, которые мы получаем, не соответствуют ожиданиям. Конечно, многие инкапсуляции могут быть выполнены на основе аксиом, чтобы отменить предыдущий запрос или обработать его путем перехвата, но начать с защиты от сотрясений относительно просто.
Что такое анти-шейк и троттлинг?
Функция Anti-Shake (Debsisis)
Объяснение: Когда событие инициируется непрерывно, функция обработки события будет выполняться только один раз, если событие не будет инициировано снова в течение определенного интервала времени.Если событие инициируется снова до наступления установленного интервала времени, задержка начнется снова.
Случай: когда событие прокрутки срабатывает непрерывно, функция дескриптора не выполняется немедленно.Если событие прокрутки не срабатывает в течение 1000 миллисекунд, функция дескриптора срабатывает один раз с задержкой.
function debounce(fn, wait) {
let timeout = null
return function() {
if(timeout !== null) clearTimeout(timeout)
timeout = setTimeout(fn, wait);
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', debounce(handle, 1000))
Второй параметр addEventListener на самом деле является методом возврата в функции debounce,let timeout = nullЭта строка кода будет выполняться только тогда, когда событие триггера выполняется один раз при выполнении addEventListener. Затем каждый раз, когда запускается событие прокрутки, последняя задержка будет очищена и будет записана новая задержка. последняя записанная задержка не будет очищена, и выполнение может быть отложено.Это принцип функции устранения дребезга
функция дроссельной заслонки
Объяснение: Когда событие запускается постоянно, обработчик события выполняется через равные промежутки времени.
Случай: когда событие прокрутки запускается постоянно, функция дескриптора не выполняется немедленно, а функция дескриптора выполняется каждые 1000 миллисекунд.
function throttle(fn, delay) {
var prev = Date.now()
return function() {
var now = Date.now()
if (now - prev > delay) {
fn()
prev = Date.now()
}
}
}
function handle() {
console.log(Math.random())
}
window.addEventListener('scroll', throttle(handle, 1000))
Принцип аналогичен функции защиты от сотрясений.Каждый раз, когда выполняется функция fn, prev обновляется для записи времени этого выполнения.Когда запускается следующее событие, оценивается, достиг ли временной интервал заданной настройки, и вышеуказанная операция повторяется.
И антитряска, и троттлинг могут использоваться для событий перемещения мыши, прокрутки, изменения размера, ввода и т. д. Разница между ними в том, что стабилизация будет выполняться только один раз в конце непрерывного цикла событий, а троттлинг будет регулярным в интервалы в течение цикла события. выполняется несколько раз.
Практика в Вью
Реализация антишейка в vue — это не что иное, как следующие два метода.
- Инкапсулирует утилиты
- Компоненты пакета
Инкапсулирует утилиты
Преобразуйте приведенный выше случай, чтобы инкапсулировать простой инструмент utils.
utils.js
let timeout = null
function debounce(fn, wait) {
if(timeout !== null) clearTimeout(timeout)
timeout = setTimeout(fn, wait)
}
export default debounce
app.js
<input type="text" @input="debounceInput($event)">
import debounce from './utils'
export default {
methods: {
debounceInput(E){
debounce(() => {
console.log(E.target.value)
}, 1000)
}
}
}
Компоненты пакета
Что касается упаковки компонентов, нам нужно использовать$listeners、$attrsЭти два атрибута, оба являются новым содержанием vue2.4, введение официального сайта относительно неясно, давайте посмотрим, что они делают:
$attrs:Когда родительский компонент вызывает дочерний компонент, он привязывает множество свойств к дочернему компоненту, а затем регистрирует и использует его через реквизиты в дочернем компоненте, затем те, которые не зарегистрированы реквизитами, будут помещены в$attrsКонечно, класс и стиль не включены, и их можно пройти черезv-bind="$attrs"Передайте внутренний компонент дочернего компонента.
$listeners:События, связанные родительским компонентом с дочерним компонентом без декоратора .native, будут помещены в$listeners里, через который можно пройтиv-on="$listeners"Передайте внутренний компонент.
Проще говоря$listeners、$attrsОни представляют собой наследование свойств и событий, что очень полезно при вторичной инкапсуляции компонентов.
Мы берем компонент el-input элемента element-ui в качестве примера для инкапсуляции компонента debounce-input с защитой от сотрясений.
debounce-input.vue
<template>
<el-input v-bind="$attrs" @input="debounceInput"/>
</template>
<script>
export default {
data() {
return {
timeout: null
}
},
methods: {
debounceInput(value){
if(this.timeout !== null) clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.$emit('input', value)
}, 1000)
}
}
}
</script>
app.vue
<template>
<debounce-input placeholder="防抖" prefix-icon="el-icon-search" @input="inputEve"></debounce-input>
</template>
<script>
import debounceInput from './debounce-input'
export default {
methods: {
inputEve(value){
console.log(value)
}
},
components: {
debounceInput
}
}
</script>
Инкапсуляция вышеперечисленных компонентов использует $attrs.Хотя разработчику не нужно обращать внимание на передачу атрибутов, все же пользоваться им неудобно, т.к. инкапсуляция el-input внутри тоже ограничена в плане стиля. Учащиеся, которым приходилось работать с высокоуровневыми компонентами React, могут знать, что высокоуровневый компонент React — это, по сути, функция, обертывающая входящий компонент React.После серии обработок он, наконец, возвращает относительно улучшенный компонент React. Итак, можем ли мы извлечь уроки из этой идеи в vue?Давайте посмотрим на функциональные компоненты vue.
О функциональных компонентах vue
Что такое функциональный компонент?
Функциональный компонент относится к использованию функции для рендеринга компонента vue. Этот компонент принимает только некоторые свойства. Мы можем пометить этот тип компонента как функциональный, что означает, что он не имеет состояния (нет данных для ответа) и не имеет экземпляра (нет этого контекста).
Функциональный компонент может выглядеть так:
export default () => {
functional: true,
props: {
// Props 是可选的
},
// 为了弥补缺少的实例, 提供第二个参数作为上下文
render: function (createElement, context) {
return vNode
}
}
Примечание. В версиях до 2.3.0 параметр props требовался, если функциональный компонент хотел получать реквизиты. В версии 2.3.0 или более поздней вы можете опустить параметр props, и все свойства компонента будут автоматически и неявно разрешены как свойства. Но как только вы зарегистрируете реквизит, в context.prop появится только зарегистрированный реквизит.
Второй параметр context функции рендеринга используется для замены контекста this, который представляет собой объект, содержащий следующие поля:
- реквизит: объект, предоставляющий все реквизиты
- Children: массив дочерних узлов VNode
- слоты: функция, которая возвращает объект, содержащий все слоты
- scopedSlots: (2.6.0+) Объект, предоставляющий переданные слоты с областью видимости. Также предоставляет обычные слоты как функции.
- data: весь объект данных, переданный компоненту, переданный в компонент в качестве второго параметра createElement
- parent: ссылка на родительский компонент
- listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。 Это псевдоним для data.on .
- инъекции: (2.3.0+) Если используется опция инъекции, этот объект содержит свойства, которые должны быть внедрены.
Что внутри vm.$slots API
Слоты используются для доступа к контенту, распределяемому слотами. Каждый именованный слот имеет свои собственные свойства (например: содержимое v-slot:foo будет найдено в vm.$slots.foo). Атрибут по умолчанию включает все узлы, не содержащиеся в именованном слоте, или содержимое v-slot:default.
слоты () против детей
Вы можете задаться вопросом, почему slots() и дочерние элементы нужны одновременно. Разве slots().default не похож на детей? В некоторых сценариях да, но как насчет функционального компонента с дочерними элементами, как показано ниже?
<my-functional-component>
<p v-slot:foo>
first
</p>
<p>second</p>
</my-functional-component>
Для этого компонента дочерние элементы дадут вам два тега абзаца, в то время как slots().default передаст только второй анонимный тег абзаца, а slots().foo передаст первый именованный тег абзаца. У него есть как дочерние элементы, так и slots(), поэтому вы можете сделать компонент осведомленным о механизме слотов или просто передать дочерние элементы и передать их другим компонентам для обработки.
Пример использования функционального компонента
Предположим, что есть компонент а, который вводит три компонента а1, а2 и а3.Родительский компонент компонента а передает атрибут типа компоненту а, чтобы определить, какой компонент отображать в а1, а2 и а3 в соответствии со значением типа компонент. Для такого сценария очень удобно использовать функциональные компоненты. Так зачем использовать функциональные компоненты? Итог: низкие накладные расходы на рендеринг, потому что функциональные компоненты — это просто функции.
Использование функциональных компонентов для защиты от тряски
Из-за деловых отношений пакет компонента защиты от сотрясений поддерживает одновременное использование ввода, кнопки, электронного ввода и электронной кнопки.Если это компонент класса ввода, он будет выполнять обработку защиты от сотрясений. к событию ввода, и если это компонент класса кнопки, он будет выполнять обработку против сотрясения для события щелчка.
const debounce = (fun, delay = 500, before) => {
let timer = null
return (params) => {
timer && window.clearTimeout(timer)
before && before(params)
timer = window.setTimeout(() => {
// click事件fun是Function input事件fun是Array
if (!Array.isArray(fun)) {
fun = [fun]
}
for (let i in fun) {
fun[i](params)
}
timer = null
}, parseInt(delay))
}
}
export default {
name: 'Debounce',
functional: true, // 静态组件 当不声明functional时该组件同样拥有上下文以及生命周期函数
render(createElement, context) {
const before = context.props.before
const time = context.props.time
const vnodeList = context.slots().default
if (vnodeList === undefined){
console.warn('<debounce> 组件必须要有子元素')
return null
}
const vnode = vnodeList[0] || null // 获取子元素虚拟dom
if (vnode.tag === 'input') {
const defaultFun = vnode.data.on.input
const debounceFun = debounce(defaultFun, time, before) // 获取节流函数
vnode.data.on.input = debounceFun
} else if (vnode.tag === 'button') {
const defaultFun = vnode.data.on.click
const debounceFun = debounce(defaultFun, time, before) // 获取节流函数
vnode.data.on.click = debounceFun
} else if (vnode.componentOptions && vnode.componentOptions.tag === 'el-input') {
const defaultFun = vnode.componentOptions.listeners.input
const debounceFun = debounce(defaultFun, time, before) // 获取节流函数
vnode.componentOptions.listeners.input = debounceFun
} else if (vnode.componentOptions && vnode.componentOptions.tag === 'el-button') {
const defaultFun = vnode.componentOptions.listeners.click
const debounceFun = debounce(defaultFun, time, before) // 获取节流函数
vnode.componentOptions.listeners.click = debounceFun
} else {
console.warn('<debounce> 组件内只能出现下面组件的任意一个且唯一 el-button、el-input、button、input')
return vnode
}
return vnode
}
}
<template>
<debounce time="300" :before="beforeFun">
<input type="text" v-model="inpModel" @input="inputChange"/>
</debounce>
</template>
<script>
import debounce from './debounce'
export default {
data() {
return {
inpModel: 1
}
},
methods: {
inputChange(e){
console.log(e.target.value, '防抖')
},
beforeFun(e){
console.log(e.target.value, '不防抖')
}
},
components: {
debounce
}
}
</script>
Принцип также очень прост: перехватывать события щелчка и ввода в vNode для обработки анти-дрожания, чтобы его было очень просто использовать.
пользовательская директива директива
Давайте подумаем над проблемой.Стык инкапсуляции функциональных компонентов и антишейка заключается в том, чтобы получить vNode, тогда мы можем получить vNode и через кастомные инструкции, и даже получить нативный Дом, так будет удобнее использовать кастомные инструкции для борьбы с . . . . . .
Связанное Чтение
$attrs or $listeners Talent.v UE JS.org/V2/API/#VM-…
函数式组件 Talent.v ue js.org/v2/expensive/hot…
自定义指令 Talent.v UE JS.org/V2/expensive/rough…