Для новичков во Vue: использование пользовательских директив Vue для завершения выбора компонента

внешний интерфейс Vue.js

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

Готовые рендеры выглядят следующим образом:

Сначала сделаем простой макет:

<template>
  <div class="select">
    <div class="inner">
      <div class="inputWrapper">
        <input type="text" readonly placeholder="请选择菜品">
        <span class="iconfont icon-zhankaishangxia"></span>
      </div>
      <ul class="options">
        <li v-for="(item, index) in options" :key="index">{{item.value}}</li>
      </ul>
    </div>
  </div>
</template> 

......

data() {
    return {
        options: [
            {
              value: '西红柿鸡蛋'
            },
            {
              value: '青椒抱鸡蛋'
            },
            {
              value: '回锅肉'
            },
            {
              value: '宫保鸡丁'
            },
            {
              value: '地三鲜'
            }
        ],
    }
}

Эффект такой:

Варианты, доступные ниже, используют абсолютное позиционирование; в то же время ввод устанавливается только для чтения, что делает ввод не вводом, а общая компоновка очень проста.

2. Начните добавлять функции

Далее мы хотим добавить две функции:

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

Функции этих двух элементов довольно просты.Давайте сначала выполним первый, щелкните поле ввода, чтобы переключить параметры отображения, и используйте v-show.

<div class="inputWrapper" @click="showOptions = !showOptions">
    <input type="text" readonly placeholder="请选择菜品">
    <span class="iconfont icon-zhankaishangxia"></span>
</div>
<ul class="options" v-show="showOptions" v-show="showOptions">  //添加v-show
    <li v-for="(item, index) in options" :key="index">{{item.value}}</li>
</ul>

.......
data() {
    showOptions: false
}

Как показано выше, добавьте в опцииv-show="showOptions"и воляshowOptionsинициализирован какfalse. При этом в упаковкеinputизdivдобавитьclickСобытия переключаются вперед и назадshowOptionsлогическое значение .

Эффект следующий:

Во-вторых, нажмите на опцию ниже, чтобы отобразить выбранную во вводе, и при этом сделать так, чтобы опции исчезли, это не сложно.

<div class="inputWrapper" @click="showOptions = !showOptions">
    <input type="text" readonly placeholder="请选择菜品" :value="selected"> //这里用value绑定一个data值selected
    <span class="iconfont icon-zhankaishangxia"></span>
</div>
<ul class="options" v-show="showOptions">
    <li v-for="(item, index) in options" :key="index" @click="choose(item.value)">{{item.value}}</li>
</ul>

......

data() {
    return {
        ......
        showOptions: false
        selected: ''
    }
},
methods: {
    choose(value) {
        this.showOptions = false
        if (value !== this.selected) {
            this.selected = value
        }
    }
}

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

Эффект следующий:

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

3. Обычные манипуляции с DOM против пользовательских директив Vue

На самом деле реализовать эту функцию несложно, но для ее решения нужно манипулировать DOM

<div class="inputWrapper" @click.stop="showOptions = !showOptions"> //注意这里的stop修饰器
    <input type="text" readonly placeholder="请选择菜品" :value="selected">
    <span class="iconfont icon-zhankaishangxia"></span>
</div>
<ul class="options" v-show="showOptions">
    <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li>  //还有这里的stop修饰器
</ul>

...
data() {
    return {
        ......
        showOptions: false
    }
}
mounted() {
    let that = this
    document.addEventListener('click', function() {
        that.showOptions = false
    })
}

Приведенный выше код имеет две точки: одна — добавить событие клика ко всему документу после монтирования, чтобы параметры можно было скрыть при нажатии, но когда мы нажимаем на поле ввода и содержимое параметра, мы не хотим, чтобы он триггер, и отпустить его с логикой, которую мы написали ранее, так что дайте дваclickдобавлены событияstopмодификаторы для предотвращения всплытия, чтобы они не всплывали при нажатииdocumentнад. Эффект следующий:

Основные функции были написаны здесь, вы можете добавить$emitа такжеpropsдля передачи данных, чтобы сделать их более общими. Но, в конце концов, мы можем улучшить функцию щелчка в других местах, чтобы часть опции исчезла, и мы можем рассмотреть возможность использования директивы Vue для ее реализации.

Что касается инструкций Vue, то в официальной документации есть четкие инструкции, если не понятно, можно нажатьздесьПроверьте это сначала!

Что касается The Vue Custom Induction, в этом примере необходимо понимать следующие базовые точки знаний:

  • Это для управления DOM, поэтому все директивы Vue висят.templateна элемент в
  • Он имеет 4 функции крючка, однаbind, он вызывается при первой привязке директивы к элементу и только один раз, этот хук очень важен, мы будем использовать его в этом примере; второй —inserted, он вызывается при вставке элемента в родительский элемент, официальная документация даетv-focusпример использует его; третий и четвертыйupdateа такжеcomponentUpdated, первый находится вvNodeВызывается при обновлении, последнее вызывается после завершения обновления; наконецunbind, вызываемый, когда директива и элемент не связаны.
  • Эти 4 функции ловушек могутМожно передать не менее 3 параметров, первыйelэлемент связанной инструкции, второйbinding,это объект,а такжеНекоторые из его свойств особенно полезны, свойства которого включаютname,expressionа такжеvalueПодождите, конечно, это не только эти три, но мы собираемся использовать их для этого примера. Например: предположим, я пишу пользовательскую директивуv-example="test", и этоtestэто яmethodsМетод написан, то вы можете пройтиbinding.nameполучатьexampleстрока, доступ к которой можно получить черезbinding.valueполучатьtestсама функция и выполнение. Неважно, если вы не понимаете здесь, мы поговорим об этом дальше.

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

хорошо, мы поставили передmountedУдалите все написанное, связанное с манипулированием DOM, и начните писать пользовательскую инструкцию вручную.

<ul class="options" v-show="showOptions" v-clickOut="test"> //这里使用了下面的自定义指令,并将一个test方法传递进去了
    <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li>
</ul>

...
methods: {
    ......
    test() {             //test函数,它作为参数传递给了指令
        console.log('这是一个测试函数')
    }
}, 
directives: {              //这里是自定义指令
    clickOut: {             // 这里是自定义的v-clickOut指令
        bind: function(el, binding) {        // bind钩子函数,当它与元素绑定的时候就会执行
            console.log('el===>', el)
            console.log('binding.name===>', binding.name)
            console.log('binding.expression===>', binding.expression)
            console.log('binding.value===>', binding.value)
        }
    }
}

Приведенный выше код четко аннотирован, мы настроилиclickOutинструкцию, и прикрепите ее к элементу, и передайтеtestспособ, посмотримconsole.logЧто выходит.

Как видно из рисунка выше, когда инструкция привязана к элементу, онаbind, он выполнит функцию привязки, чтобы получить много полезного, о чем мы говорили вышеbindВ функции есть несколько важных параметров.Из распечатанного результата очень хорошо видно, что el — это сам элемент, связанный инструкцией, а binding — это объект, который получает много полезного, включая переданную функцию.

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

<ul class="options" v-show="showOptions" v-clickOut="test">
    <li v-for="(item, index) in options" :key="index" @click.stop="choose(item.value)">{{item.value}}</li>
</ul>
...

methods: {
    test() {
        this.showOptions = false   
    }
},
directives: {
    clickOut: {
      bind: function(el, binding) {
        document.addEventListener('click', function(e) {
          if (el.contains(e.target)) return false
          if (binding.expression) {
            binding.value()
          }
        })
      }
    }

Видите, что делает приведенный выше переписанный код?Скажи логику: когда мы делаем свои собственныеv-clickOutПри привязке к элементу ul в секции options слушаем событие клика документа.Если нажатый элемент является дочерним элементом элемента, привязанного по инструкции или сам привязанный элемент, то ничего не делаем, если нет, то То выполнить переданную тестовую функцию. Результатом выполнения тестовой функции является частичное скрытие опций.

Логика ясна.

Конечно, мы можем продолжать его улучшать. мы даемdocument.addEventListener, вы также можетекогда придет времяremoveEventListener, подходящее времяunbindфункция крючка.

Итак, мы можем улучшить его:

......

directives : {
    clickOut: {
        bind: function(el, binding) {
            function handler(e) {
              if (el.contains(el.target)) return false
              if (binding.expression) {
                binding.value()
              }
            }
            el.handler = handler
            document.addEventListener('click', el.handler)
        },
        unbind: function(el) {
            document.removeEventListener('click', el.handler)
        }        
    }
}

Код такой же, как и выше, а эффект следующий:

4. Дополнения и модификации

Прочитав комментарий друга ниже, я обнаружил, что при множественном выборе все еще есть небольшая проблема:

При наличии нескольких вариантов выбора развернутые параметры исчезнут только при нажатии других пустых мест.Когда один из параметров развернут, а другой выбран, ранее развернутые параметры не будут свернуты.Причина этой проблемы в том, что щелчокinputЧастично заблокирован от пузырей. Просто удалите его и разверните элемент, привязанный указанной директивой, ко всему выбору, а не только к разделу параметров, потому что в директиве есть перехват.

<template>
  <div class="select">
    <div class="inner" v-clickOut="test">  //指令放到了这里
      <div class="inputWrapper" @click="showOptions = !showOptions">  //这里阻止冒泡被去掉了
        <input type="text" readonly placeholder="请选择菜品" :value="selected">
        <span class="iconfont icon-zhankaishangxia"></span>
      </div>
      <ul class="options" v-show="showOptions">
        <li v-for="(item, index) in options" :key="index" @click="choose(item.value)">{{item.value}}</li>  // 这里的点击阻止冒泡也被去掉了
      </ul>
    </div>
  </div>
</template>

Эффект:

Наконец, введите адрес github:Модифицированный кодовый адрес

Краткое резюме: Это очень простой и небольшой пример, потому что нам нужно манипулировать DOM, поэтому мы выбираем использование custom для завершения, конечно, мы также можем использовать другие методы. Просто когда мы используем Vue, если нам нужно манипулировать DOM, мы можем подумать о том, можно ли этого добиться с помощью пользовательских инструкций. Это моя седьмая статья о Наггетс, спасибо за прочтение!