Научу вас трюку Vue, я не говорю об обычных людях

Vue.js

Эта статья была опубликована сообществом cloud+community

1. Спрос

В недавних проектах необходимо динамически отображать текстовое поле с одним или несколькими вариантами выбора с полем подсказки в структуре vue. Конкретный эффект показан на следующем рисунке. Когда поле ввода сфокусировано, внешний интерфейс Компонент отображает параметры через полученный параметр kv.Когда пользователь щелкает параметр, ключ выбранного параметра может быть собран в поле ввода, позволяя пользователю вводить свободно.

img

Из-за элемента-интерфейса, используемого в проекте, компоненты ввода и выбора компонента являются предпочтительными, однако при фактическом использовании обнаруживается, что компоненты, предоставляемые фреймворком, не могут полностью удовлетворить это требование. Например, использование компонента ввода с предложениями ввода может реализовать окна подсказок и одиночный выбор, но не удобно реализовывать множественный выбор (повторяющийся выбор закроет содержимое поля ввода).

img

Использование функции удаленного поиска селектора выбора, предоставляемого платформой, может реализовать окно подсказки и может легко реализовать одиночный выбор и множественный выбор, но содержимое компонента выбора может быть выбрано только пользователем (содержимое текстового поля должен быть включен в параметр подсказки), пользователям не разрешается свободно вводить текстовое содержимое.

img

Кроме того, в проекте проекта необходимо реализовать макет из трех столбцов, а окончательный возвращаемый результат должен динамически собирать ключевое значение параметра.Если стоимость преобразования существующего компонента элемента слишком высока, попытайтесь инкапсулировать одно- текстовое поле выбора/множественного выбора с полем подсказки Компоненты, фиксируйте проблемы, возникающие при взаимодействии компонентов в процессе упаковки.

2. Дизайн параметров интерфейса

Компонент поддерживает передачу 6 параметров, которые

  1. размер (размер, String, средний/маленький/мини)
  2. значение (входное значение, строка, двусторонняя привязка может быть достигнута с помощью модификатора синхронизации)
  3. opt (список опций, массив, массив kv, например {key:1, value:xxx})
  4. разделитель (разделитель, строка, например ',', '|', '-')
  5. множественный (поддерживать ли множественный выбор, логическое значение)
  6. заполнитель (подсказка, строка)

Метод вызова следующий:

<cs-select
  size="mini" // 尺寸
  :value.sync="value" // value
  :opt="optParams.kv" // 选项 
  seperator="," // 分隔符
  :multiple="true">
</cs-select>

3. В окне подсказки показана реализация скрытого взаимодействия.

Чтобы уточнить вышеуказанные требования, необходимо отображать окно подсказки, когда пользователь щелкает поле ввода (получает фокус), скрывать окно подсказки, когда пользователь щелкает пустую область, и ничего не делать, когда щелкается сам компонент. . Структура шаблона компонента следующая, отображение и скрытие окна подсказки управляется переменной show, а события focus и defocus привязаны к полю ввода компонента:@focus="onfocus"а также@blur="onblur", установите для this.show значение true, когда фокус, и false, когда размытие, потому что щелчок по элементу option за пределами поля ввода неизбежно приведет к тому, что поле ввода будет не в фокусе и автоматически закроется, ключ ко всем проблемам заключается в том, как реализовать нажмите кнопку подсказки, не скрывая окно подсказки.

<template>
  <div>
  	<!-- 输入框 -->
    <el-input
      @focus="onfocus
      @blur="onblur>
    </el-input>
    <!-- 提示框 -->
    <div v-if="show && opt.length > 0">
      <el-row>
        <el-col :span="8" v-for="(item, index) in opt" :key="index">
          {{item.value}}
        </el-col>
      </el-row>
    </div>
  </div>
</template>

3.1 Схема попытки 1: активный фокус на событии клика

По вышеизложенным требованиям можно не сомневаться, что можно привязать событие клика к опции и вызвать эл-вводfocus()Метод выполняет активную фокусировку, которая реализована следующим образом: здесь ссылка на vue используется для поиска элемента dom через $ref.

clickEvent () {
  this.show = true // 设置提示框显示
  this.$refs.input.$el.querySelector('input').focus() // 设置主动聚焦
}

вопрос:В реальном процессе разработки было обнаружено, что после каждого нажатия на опцию подсказки окно подсказки будет мигать один раз, причина в механизме событий js,blurсобытие предшествуетclickСобытие выполняется, в результате чего окно подсказки скрывается, а затем отображается, что приводит кмерцание.

3.2 Попытка схемы 2: добавить к событию размытия переменную delayer + switch

Из-за варианта 1blurсобытие предшествуетclickвыполнение события, поэтому рассмотрите возможность использованияsettimeoutDelayer для изменения времени выполнения, реализация выглядит следующим образом.

blurEvent () {
  setTimeout(() => {
    this.show = false
  }, 200)
}

**Проблема: **В реальном процессе разработки было обнаружено, что задержка задерживает операцию закрытия, так что после того, как поле ввода получает фокус, оно активно закрывает окно подсказки и больше не открывается автоматически, что не соответствует потребности, поэтому рассмотрите возможность использования переменной switch.canCloseЧтобы определить, нужно ли выполнять отключение в данный момент, реализация выглядит следующим образом.

focusEvent () {
  this.show = true
  this.canClose = true // 聚焦时打开开关
},
blurEvent () {
  if (this.canClose) {
    setTimeout(() => {
      this.show = false // 只有开关打开时才执行关闭
    }, 200)
  }
},
clickEvent (key) {
  this.canClose = false // 点击提示选项,关闭开关
  this.show = true
  ...
}

вопрос:В реальном процессе разработки обнаруживается, что в большинстве случаев окно подсказки можно отображать и скрывать, но когда операция выполняется быстро, оно будет отображаться и скрываться.Иногда окно подсказки не может быть закрыто или закрыто заранееПричина анализа заключается в том, что любое срабатывание переключателя в течение периода задержки может привести к изменению состояния переключателя компонента, вызывая беспорядочное состояние.

3.3 План попытки 3: не использовать размытие, изменить метод закрытия на делегирование события и динамически привязать класс

Если выключение не использует размытие, а запускается событием щелчка, описанной выше проблемы синхронизации не будет.Поэтому рассмотрите возможность глобального делегирования событий, отслеживания события щелчка пользователя и закрытия окна подсказки, оценивая специальный класс узел Реализация следующая.

$('body').on('click', (event) => {
  this.show = false
})
$('body').on('click', className, (event) => {
  this.show = true
})

Вопрос 1:Делегирование событий, используя фиксированный класс, при одновременном рендеринге нескольких компонентов невозможно управлять переключением окна подсказки отдельно, поэтомуНевозможно отобразить несколько компонентов, поэтому класс использует динамическую привязку, а каждый компонент использует другой класс, который реализован следующим образом.

**Вопрос 2.** Предотвращение всплытия. Если родительский контейнер компонента предотвращает всплывание, метод закрытия, связанный с телом, не может быть запущен, и его необходимо обрабатывать отдельно для родительского контейнера.

let randId = Math.round(Math.random()*100000)
this.className = `cs-select-${randId}`
// 单独处理父容器,在父容器上绑定关闭事件
...

Модифицированные компоненты кажутся в основном пригодными для использования на поверхности, но на самом деле есть много проблем:

**Вопрос 1:** Событие привязано к родительскому компоненту в компоненте, что нарушает Закон Деметры шаблона проектирования.Повышенная связь между компонентами, что не способствует последующему обслуживанию.

**Вопрос 2.** Вышеупомянутая операция учитывает только закрытие события клика, игнорируя другие возможные ситуации закрытия, такие как использованиеtabСкрытое окно подсказки также должно отображаться нормально, когда поле ввода переключается клавишей.

**Проблема 3.** Слишком большое количество событий привязки приведет к снижению производительности и даже к неожиданным проблемам.

3.4 Попробуйте решение 4: onfocus + onblur + mousedown + переключатель

Поскольку событие фокуса выполняется до события щелчка, возникают проблемы вышеприведенной схемы 1 и схемы 2. Из данных видно, что,mousedownСобытие выполняется до события focus.Поэтому использование onfocus + onblur + mousedown + switch может хорошо решить указанную выше проблему времени выполнения.Конкретная реализация выглядит следующим образом.

focusEvent () {
  this.show = true
  this.canClose = true // 聚焦时打开开关
},
blurEvent () {
  if (this.canClose) {
    this.show = false // 只有开关打开时才执行关闭
  }
},
mousedownEvent (key) {
  this.canClose = false // 点击提示选项,关闭开关
  this.show = true
  this.$refs.input.$el.querySelector('input').focus()
  ...
}

**Проблема:**В процессе реальной разработки было обнаружено, что, поскольку компонент визуализируется динамически, элемент dom текущего объекта не может быть напрямую получен в событии mousedownEvent.this.$refs.xxx, вызывая сбой автофокуса.

3.5 План реализации

На основе схемы 4 используютnextTickАсинхронная очередь обновленийОн может решить проблему тайминга рендеринга dom, а конкретную реализацию можно немного модифицировать по схеме 4.

$nextTick: В официальном подробном подробном принципе реагирования Vue поясняется, что реализация отзывчивости Vue не означает, что DOM изменяется сразу после изменения данных, но выполняет отложенный обратный вызов после окончания следующего цикла обновления DOM и использует его после изменения данные.nextTick,则可以在回调中获取更新后的 DOM,官方示例:<https://cn.vuejs.org/v2/guide/reactivity.html#search-query-sidebar>focusEvent () {   this.show = true   this.canClose = true // 聚焦时打开开关 }, blurEvent () {   if (this.canClose) {     this.show = false // 只有开关打开时才执行关闭   } }, mousedownEvent (key) {   this.canClose = false // 点击提示选项,关闭开关   this.show = true   this.nextTick(() => { this.refs.input.el.querySelector('input').focus() }) ... } 4. Двусторонняя привязка данных компонента

Чтобы облегчить обработку данных в компоненте, значение входного значения входящего компонента сначала будет разбито на массив ключей, а затем добавлен наблюдатель-наблюдатель, чтобы отслеживать изменение входного значения, обновлять выбранное состояние компонента. окно подсказки и пройти$emitМетод синхронизируется с родительским компонентом для реализации двусторонней привязки данных.Вахта входного значения выглядит следующим образом:

watch: {
  inputVal: {
    handler () {
      let selectArray = this.inputFilter()
      this.inputVal = selectArray.join(this.seperator)
      // 更新选中状态
      this.updateActive()
      // 同步数据
      this.$emit('update:value', this.inputVal) // 可改为v-model
    },
    immediate: true
  }
}

5. Применение компонентов и доработка

Существует много сценариев применения для компонента текстового поля с одним или несколькими вариантами выбора с окном подсказки. Типичный сценарий — инкапсуляция селектора для корпоративных контактов. Когда пользователь вводит ключевое слово имени пользователя, в окне подсказки отображаются соответствующие контакты , при этом позволяя пользователю свободно вводить имя пользователя.

Компоненты, такие как:

  1. Текущий дизайн предотвращает закрытие окна подсказки путем отслеживания нажатия кнопки мыши, что, очевидно, несовместимо с мобильным терминалом.Вы можете рассмотреть возможность добавления сенсорных событий;
  2. С точки зрения макета CSS удобство для пользователя не оценивается, и в крайних случаях оно может выходить за рамки экрана;
  3. Слоты слотов и динамически настраиваемые классы пока не поддерживаются.

Его можно постепенно улучшать с итерацией всего проекта.

Эта статья была разрешена автором для публикации Tencent Cloud + Community