使用 Popper Js 创建 Vue 组件下拉列表

Creating Vue Component Dropdown with Popper Js

我正在使用 vue 和 popperjs 制作下拉组件。众所周知,我正在使用 vuejs v.2.6.12popperjs v.2.9.2,这是代码

<template>
        <button type="button" @click="show = true">
            <slot />
            <portal v-if="show" to="dropdown">
                <div>
                    <div
                        style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: .2"
                        @click="show = false"
                    />
                    <div
                        ref="dropdown"
                        style="position: absolute; z-index: 99999;"
                        @click.stop="show = autoClose ? false : true"
                    >
                        <slot name="dropdown" />
                    </div>
                </div>
            </portal>
        </button>
    </template>
    
    <script>
    import { createPopper } from "@popperjs/core";
    
    export default {
        props: {
            placement: {
                type: String,
                default: "bottom-end"
            },
            boundary: {
                type: String,
                default: "scrollParent"
            },
            autoClose: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                show: false
            };
        },
        watch: {
            show(show) {
                if (show) {
                    this.$nextTick(() => {
                        this.popper = createPopper(this.$el, this.$refs.dropdown, {
                            placement: this.placement,
                            modifiers: [
                                {
                                    name: "preventOverflow",
                                    options: {
                                        boundary: this.boundary
                                    }
                                }
                            ]
                        });
                    });
                } else if (this.popper) {
                    setTimeout(() => this.popper.destroy(), 100);
                }
            }
        },
        mounted() {
            document.addEventListener("keydown", e => {
                if (e.keyCode === 27) {
                    this.show = false;
                }
            });
        }
    };
    </script>

当我尝试 运行 代码时,我收到错误消息 Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element. 我不知道为什么会出现此错误,我想我已将参考 this.$el 和 popper this.$refs.dropdown 放在正确的位置。

有人可以帮我解决这个问题吗?
谢谢

我创建了一个没有提到的错误的片段。

  • 你使用了vue-portal:如果你的代码中没有全局导入,那么你必须将它导入到SFC
  • 您必须在某处创建 <portal-target>,否则 this.$refs.dropdown 将不存在,因为它是 portal.
  • 的默认内容

const createPopper = Popper.createPopper

/* import {
  createPopper
} from "@popperjs/core"; */

Vue.component('DropDown', {
  props: {
    placement: {
      type: String,
      default: "bottom-end"
    },
    boundary: {
      type: String,
      default: "scrollParent"
    },
    autoClose: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      show: false
    };
  },
  watch: {
    show(show) {
      if (show) {
        this.$nextTick(() => {
          this.popper = createPopper(this.$el, this.$refs.dropdown, {
            placement: this.placement,
            modifiers: [{
              name: "preventOverflow",
              options: {
                boundary: this.boundary
              }
            }]
          });
        });
      } else if (this.popper) {
        setTimeout(() => this.popper.destroy(), 100);
      }
    }
  },
  mounted() {
    document.addEventListener("keydown", e => {
      if (e.keyCode === 27) {
        this.show = false;
      }
    });
  },
  template: `
    <button type="button" @click="show = true">
      <slot />
      <portal
        v-if="show"
        to="dropdown"
      >
        <div>
          <div
            style="position: fixed; top: 0; right: 0; left: 0; bottom: 0; z-index: 99998; background: black; opacity: .2"
            @click="show = false"
          />
          <div
            ref="dropdown"
            style="position: absolute; z-index: 99999;"
            @click.stop="show = autoClose ? false : true"
          >
            <slot name="dropdown" />
          </div>
        </div>
      </portal>
    </button>
  `
})

new Vue({
  el: "#app",
  template: `
    <div>
      <drop-down>
        <template
          v-slot:default
        >
          Dropdown default slot
        </template>
        <template
          v-slot:dropdown
        >
          This is the dropdown slot content
        </template>
      </drop-down>
      <portal-target
        name="dropdown"
      />
    </div>
  `
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script src="http://unpkg.com/portal-vue"></script>
<script src="https://unpkg.com/@popperjs/core@2"></script>
<div id="app"></div>