復現場景, 看圖

分析原因
為簡單起見, 把選項區域描述為popperEl
解決方案
方案一
我最初想到的解決方案是通過css解決,通過popper-class屬性給Select下拉框添加類名,然后用css來做, 試了一下這個方案并不可行(只能在某些特定的場景下起作用),遂放棄,可能最優雅最高性能的方法就是用css來搞定, 有踩過這個坑的朋友請指點一下
方案二
通過監聽$root的scroll事件,利用事件冒泡,只需要在根元素上添加scroll事件的監聽就可以了, 測試一番之后, 發現scroll事件根本不支持冒泡, event.bubbles為false)。
方案三
通過查看element-ui 的select.vue, 發現控制popperEl顯隱的是visible 和 emptyText這兩個實例屬性, 很明顯, emptytext是不能動的, 只能在visible上動手腳了. 這里放一小段源碼
<transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="doDestroy"> <el-select-menu  ref="popper"  :append-to-body="popperAppendToBody"  v-show="visible && emptyText !== false">  <el-scrollbar   tag="ul"   wrap-class="el-select-dropdown__wrap"   view-class="el-select-dropdown__list"   ref="scrollbar"   :class="{ 'is-empty': !allowCreate && query && filteredOptionsCount === 0 }"   v-show="options.length > 0 && !loading">   <el-option    :value="query"    created    v-if="showNewOption">   </el-option>   <slot></slot>  </el-scrollbar>  <p   class="el-select-dropdown__empty"   v-if="emptyText &&    (!allowCreate || loading || (allowCreate && options.length === 0 ))">   {{ emptyText }}  </p> </el-select-menu></transition>全局搜索this.visible, 發現了這個方法
handleClose() {  this.visible = false;},這下好辦了, 按圖索驥, 順藤摸瓜, 找到這個
<template> <div class="el-select" :class="[selectSize ? 'el-select--' + selectSize : '']" @click.stop="toggleMenu" v-clickoutside="handleClose"> 后面的省略...
找到v-clickoutside指令之后, 豁然開朗 原來點擊其他區域的時候, popperEl會自動關閉的奧秘在這里, 結合方案二的靈感, 現給出如下代碼.
// src/mixins/fackClickOutSide.jslet lock = true;let el = null;const MousedownEvent = new Event('mousedown', {bubbles:true});const MouseupEvent = new Event('mouseup', {bubbles:true});const fakeClickOutSide = () => { document.dispatchEvent(MousedownEvent); document.dispatchEvent(MouseupEvent); lock = true; // console.log('dispatchEvent');};const mousedownHandle = e => { let classList = e.target.classList; if(classList.contains('el-select__caret') || classList.contains('el-input__inner')) {  lock = false;  return; } if(lock) return; fakeClickOutSide();};const mousewheelHandle = e => { if(lock || e.target.classList.contains('el-select-dropdown__item') || e.target.parentNode.classList.contains('el-select-dropdown__item')) return; fakeClickOutSide();};const eventListener = (type) => { el[type + 'EventListener']('mousedown', mousedownHandle); window[type + 'EventListener']('mousewheel', mousewheelHandle); window[type + 'EventListener']('DOMMouseScroll', mousewheelHandle); // fireFox 3.5+ }export default { mounted() {  el = this.$root.$el;  el.addFakeClickOutSideEventCount = el.addFakeClickOutSideEventCount || 0;  (! el.addFakeClickOutSideEventCount) && this.$nextTick(() => {   eventListener('add');  });  el.addFakeClickOutSideEventCount += 1; }, destroyed() {  eventListener('remove');  el.addFakeClickOutSideEventCount -= 1; },}使用姿勢
建議在根組件上混合進去, 當然,你也可以在需要的組件上去混合(不太建議, 這點代碼性能損耗應該不大吧, 哈哈哈)
// src/App.vueimport fakeClickOutSide from '@/mixins/fakeClickOutSide.js'export default {  name: 'App',  mixins: [fakeClickOutSide],}測試
常規基礎用法 和 自定義模板用法(模板內沒有嵌套的標簽) 均完美通過.
自定義模板內如果嵌套多級標簽, 需要在標簽上添加標記,然后在mousewheel事件回調里判斷是否有這個標記.
總結
依然存在的問題(隱患):
感謝一位大佬長期以來給予的幫助.
新聞熱點
疑難解答