Vue 中自定义指令的力量

Avatar of Sarah Drasner
Sarah Drasner

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费积分!

当你最初学习一个 JavaScript 框架时,感觉有点像在糖果店里的孩子。 你会吸收所有你能得到的东西,而且一开始,就会有一些东西可以让你的开发者生活更轻松。 然而,不可避免地,我们都会在使用框架时达到一个点,我们会遇到框架不能很好地覆盖的用例。

Vue 的美妙之处在于它拥有丰富的功能。 但即使你遇到了框架没有覆盖的边缘情况,它也能为你提供支持,因为你可以很容易地创建一个自定义指令。

什么是指令?

我已经在这里写过一篇关于指令的文章在我的 Vue.js 指南中,但让我们复习一下。

指令是你可以附加到 DOM 元素的微型命令。 它们以 v- 为前缀,让库知道你正在使用一个特殊的标记,并保持语法一致。 如果你需要低级别的 DOM 元素访问权限来控制一些行为,它们通常很有用。

如果你使用过 Vue(或者 Angular),你可能已经熟悉了一些指令,比如 v-ifv-elsev-show 等。 **我们将深入了解一些基础知识,但如果你想直接阅读示例,你可以 向下滚动页面一点,仍然可以理解这些概念。**

以下是一些使用指令的方法,以及一个示例对应物。 这些示例不是强制性的,只是用例。 这里的“示例”代替了实际的指令。

v-example – 这将实例化一个指令,但不接受任何参数。 不传递值,这将不是很灵活,但你仍然可以将一些功能挂在 DOM 元素上。

v-example="value" – 这将向指令传递一个值,指令根据该值确定要执行的操作。

<div v-if="stateExample">I will show up if stateExample is true</div>

v-example="'string'" – 这将允许你使用字符串作为表达式。

<p v-html="'<strong>this is an example of a string in some text</strong>'"></p>

v-example:arg="value" – 这允许我们向指令传递一个参数。 在下面的示例中,我们绑定到一个类,并使用一个单独存储的对象对其进行样式设置。

<div v-bind:class="someClassObject"></div>

v-example:arg.modifier="value" – 这允许我们使用修饰符。 下面的示例允许我们对单击事件调用 preventDefault()

<button v-on:submit.prevent="onSubmit"></button>

理解自定义指令

现在我们看到了使用指令的所有方法,让我们分解一下如何使用我们自己编写的自定义指令来实现它们。 一个很好的例子是你可以使用自定义指令来实现 scroll 事件,让我们看看如何编写它。

在最基本的情况下,这是我们创建全局指令的方式。(但它还不做任何事情——还没有!)——它只是创建了指令。

Vue.directive('tack');

在元素本身,它看起来像

<p v-tack>This element has a directive on it</p>

我们有一些钩子可用,每个钩子都有几个参数选项。 钩子如下

  • bind – 指令附加到元素后,此操作将发生一次。
  • inserted – 当元素插入到父 DOM 中时,此操作将发生一次。
  • update – 当元素更新时,此操作将被调用,但子元素尚未更新。
  • componentUpdated – 当组件子元素都更新后,此操作将被调用。
  • unbind – 当指令被移除时,此操作将被调用。
directives hooks diagram

就我个人而言,我认为 bindupdate 是五个中最有用的。

它们都有 elbindingvnode 参数可用,除了 updatecomponentUpdated,它们还会公开 oldVnode,以区分传递的旧值和新值。

el,正如你所料,是绑定所在的元素。 binding 是一个对象,它包含传递给钩子的参数。 有许多可用的参数,包括 namevalueoldValueexpressionargmodifiersvnode 具有更不寻常的用例,如果你需要直接引用虚拟 DOM 中的节点,它可用。 bindingvnode 都应视为只读。

构建自定义指令

现在我们已经分解了它,我们可以开始看看如何在实际中使用自定义指令。 让我们从刚刚介绍的第一个示例开始构建,使其更有用

Vue.directive('tack', {
 bind(el, binding, vnode) {
    el.style.position = 'fixed'
  }
});

在 HTML 本身上

<p v-tack>I will now be tacked onto the page</p>

这还可以,但它并不灵活,直到我们向它传递一个值并更新它或动态地重复使用它。 让我们决定我们希望将元素固定到顶部的距离。

Vue.directive('tack', {
  bind(el, binding, vnode) {
    el.style.position = 'fixed'
    el.style.top = binding.value + 'px'
  }
});
<div id="app">
  <p>Scroll down the page</p>
  <p v-tack="70">Stick me 70px from the top of the page</p>
</div>

查看 Pen。

假设我现在想区分我们是将 70px 的偏移量从顶部还是从左侧进行偏移。 我们可以通过传递一个参数来做到这一点

<p v-tack:left="70">I'll now be offset from the left instead of the top</p>
Vue.directive('tack', {
  bind(el, binding, vnode) {
    el.style.position = 'fixed';
    const s = (binding.arg == 'left' ? 'left' : 'top');
    el.style[s] = binding.value + 'px';
  }
});

查看 Pen。

你也可以使用多个值。 你可以用与常规指令相同的方式来做到这一点

<p v-tack="{ top: '40', left: '100' }">Stick me 40px from the top of the page and 100px from the left of the page</p>

然后指令将被重写以同时使用两者

Vue.directive('tack', {
  bind(el, binding, vnode) {
    el.style.position = 'fixed';
    el.style.top = binding.value.top + 'px';
    el.style.left = binding.value.left + 'px';
  }
}); 

查看 Pen。

我们还可以编写更复杂的内容,我们可以根据自定义指令创建和修改方法。 在这里,我们将做类似于 waypoints 的示例,我们可以使用少量代码创建一个在特定滚动事件触发时触发的动画

Vue.directive('scroll', {
  inserted: function(el, binding) {
    let f = function(evt) {
      if (binding.value(evt, el)) {
        window.removeEventListener('scroll', f);
      }
    };
    window.addEventListener('scroll', f);
  },
});

// main app
new Vue({
  el: '#app',
  methods: {
   handleScroll: function(evt, el) {
    if (window.scrollY > 50) {
      TweenMax.to(el, 1.5, {
        y: -10,
        opacity: 1,
        ease: Sine.easeOut
      })
    }
    return window.scrollY > 100;
    }
  }
});
<div class="box" v-scroll="handleScroll">
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A atque amet harum aut ab veritatis earum porro praesentium ut corporis. Quasi provident dolorem officia iure fugiat, eius mollitia sequi quisquam.</p>
</div>

查看 Pen。

在这些 Pen 中,我们保持所有内容简单,以便你能轻松地看到它。 在实际的应用程序中,你可以构建出非常好的自定义灵活的自定义指令,可供你的整个团队使用。

在实际的构建过程中,我会将指令代码放在位于 src 目录根目录的 main.js 文件中(如果你使用的是像 Vue-cli 这样的构建工具),这样 App.vue 以及组件目录中的所有后续 .vue 文件都可以访问它。 还有其他使用它的方法,但我发现这是对整个应用程序最灵活的实现。

如果你想了解有关 Vue 框架的更多信息,请查看我们的 指南。