当你最初学习一个 JavaScript 框架时,感觉有点像在糖果店里的孩子。 你会吸收所有你能得到的东西,而且一开始,就会有一些东西可以让你的开发者生活更轻松。 然而,不可避免地,我们都会在使用框架时达到一个点,我们会遇到框架不能很好地覆盖的用例。
Vue 的美妙之处在于它拥有丰富的功能。 但即使你遇到了框架没有覆盖的边缘情况,它也能为你提供支持,因为你可以很容易地创建一个自定义指令。
什么是指令?
我已经在这里写过一篇关于指令的文章,在我的 Vue.js 指南中,但让我们复习一下。
指令是你可以附加到 DOM 元素的微型命令。 它们以 v-
为前缀,让库知道你正在使用一个特殊的标记,并保持语法一致。 如果你需要低级别的 DOM 元素访问权限来控制一些行为,它们通常很有用。
如果你使用过 Vue(或者 Angular),你可能已经熟悉了一些指令,比如 v-if
、v-else
、v-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
– 当指令被移除时,此操作将被调用。
就我个人而言,我认为 bind
和 update
是五个中最有用的。
它们都有 el
、binding
和 vnode
参数可用,除了 update
和 componentUpdated
,它们还会公开 oldVnode
,以区分传递的旧值和新值。
el
,正如你所料,是绑定所在的元素。 binding
是一个对象,它包含传递给钩子的参数。 有许多可用的参数,包括 name
、value
、oldValue
、expression
、arg
和 modifiers
。 vnode
具有更不寻常的用例,如果你需要直接引用虚拟 DOM 中的节点,它可用。 binding
和 vnode
都应视为只读。
构建自定义指令
现在我们已经分解了它,我们可以开始看看如何在实际中使用自定义指令。 让我们从刚刚介绍的第一个示例开始构建,使其更有用
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>
假设我现在想区分我们是将 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';
}
});
你也可以使用多个值。 你可以用与常规指令相同的方式来做到这一点
<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';
}
});
我们还可以编写更复杂的内容,我们可以根据自定义指令创建和修改方法。 在这里,我们将做类似于 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 中,我们保持所有内容简单,以便你能轻松地看到它。 在实际的应用程序中,你可以构建出非常好的自定义灵活的自定义指令,可供你的整个团队使用。
在实际的构建过程中,我会将指令代码放在位于 src
目录根目录的 main.js
文件中(如果你使用的是像 Vue-cli 这样的构建工具),这样 App.vue
以及组件目录中的所有后续 .vue
文件都可以访问它。 还有其他使用它的方法,但我发现这是对整个应用程序最灵活的实现。
如果你想了解有关 Vue 框架的更多信息,请查看我们的 指南。
只是想花点时间感谢你发布了这篇文章,干得不错。
太棒了! 我以前不知道自定义指令。
你会选择在什么情况下使用指令而不是组件?