Vue.js 自定义指令 : $refs is empty

标签 vue.js vuejs2 vue-directives

背景

我按照以下指南创建了一个自定义指令(用于检测元素外的点击):https://tahazsh.com/detect-outside-click-in-vue .

元素:

<button
 id='burger'
 ref='burger'
 class='burger button is-white'>
 <span class='burger-top' aria-hidden='true' style='pointer-events: none;'></span>

 <span class='burger-bottom' aria-hidden='true' style='pointer-events: none;'></span>
</button>

指示:
import Vue from 'vue';

let handleOutsideClick;

Vue.directive('outside', {
  bind(el, binding, vnode) {
    setTimeout(() => {
      handleOutsideClick = (e) => {
        e.stopPropagation();

        const { handler, exclude } = binding.value;

        let clickedOnExcludedEl = false;

        exclude.forEach((refName) => {
          if (!clickedOnExcludedEl) {
            const excludedEl = vnode.context.$refs[refName]
            console.log(vnode.context.$refs);
            clickedOnExcludedEl = excludedEl.contains(e.target);
          }
        });

        if (!el.contains(e.target) && !clickedOnExcludedEl) {
          vnode.context[handler]();
        }
      };
      document.addEventListener('click', handleOutsideClick);
      document.addEventListener('touchstart', handleOutsideClick);
    }, 50);
  },
  unbind() {
    document.removeEventListener('click', handleOutsideClick);
    document.removeEventListener('touchstart', handleOutsideClick);
  },
});

请注意,我必须添加 setTimeout .

在侧边栏组件中使用指令:
<div
  id='sidebar'
  class='sidebar-container'
  v-show='toggleSideBar'
  v-outside='{
    handler: "close",
    exclude: [
      "burger",
    ],
  }'>
  <div class='sidebar-content'>
    // other content
  </div>
</div>

问题
$refs为空,即使该元素显然具有 ref .

console.log(vnode.context.$refs); 也可以清楚地看出这一点.

值得注意的是,通过 id 获取元素是有效的:
        exclude.forEach((id) => {
          if (!clickedOnExcludedEl) {
            const excludedEl = document.getElementById(id);
            clickedOnExcludedEl = excludedEl.contains(e.target);
          }
        });

问题

为什么是 $refs当元素明确存在并且可以通过 id 访问时为空?

最佳答案

此代码目前的编写方式 ref='burger'v-outside必须在同一个模板中。

关键线是:

const excludedEl = vnode.context.$refs[refName]
vnode.context将引用侧边栏组件的 Vue 实例。 $refs必须在该组件的模板中定义。
$refs是特定 Vue 实例的本地。 “Vue 实例”是指组件实例,如果该术语更清楚的话。所以侧边栏会有一组$refs导航栏将有自己的一套 $refs .

根据问题下的评论,该按钮目前似乎是在导航栏组件的模板中定义的。所以你最终会得到一个 burger导航栏中的引用 $refs但指令正在侧边栏的 $refs 中寻找它.

该指令的编写方式需要一个类似这样的模板:

<div>
  <div
    v-outside='{
      handler: "close",
      exclude: ["burger"]
    }'
  >
    ...  
  </div>
  <button ref='burger'>...</button>
</div>

这里v-outsideref='burger'都在同一个模板中,所以一切都会正常工作。一旦您将按钮移动到不同的模板,它将无法找到它。

使用 iddocument.getElementById是一个非常不同的场景。元素 ID 是全局注册的。元素在 DOM 中的位置并不重要,只要它存在就会被找到。

关于Vue.js 自定义指令 : $refs is empty,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59212245/

相关文章:

vue.js - 什么是 Vuetify v-menu 组件 'save' 方法?

javascript - Quasar Q-Table 如何获得过滤或排序的行?

vue.js - 在 VueJs 的对象中添加新属性时如何触发更改?

javascript - [Vue 警告] : Error in created hook: "TypeError: Cannot set property of undefined"

vue.js - 有条件地渲染指令

javascript - Vue.js:在 vue.js 中使用带有条件渲染的 aria-controls

javascript - 如何更改照片中文字的颜色?

javascript - Vuejs : weird class render

vue.js - 为什么 Vue 的 v-model 在点击事件触发后更晚才更新数据?