Vue.js 是一个构建数据驱动的 web 界面的库,它可以帮助开发者快速构建出功能丰富的 web 应用。Vue.js 提供了一种自定义指令的方式来扩展 HTML 元素的行为,这样可以让我们在不修改核心代码的情况下,实现一些特定的功能。
Vue.js 自定义指令是一个函数,该函数接受三个参数:el、binding、vnode。el 参数代表当前 DOM 元素;binding 参数包含了一些属性信息;vnode 参数包含 Vue 编译生成的虚拟节点。
要使用 Vue.js 自定义指令,首先要在 Vue 实例中注册该指令。我们可以使用 Vue.directive() 方法来注册自定义指令:
Vue.directive('my-directive', { bind(el, binding, vnode) { // 这里可以处理 DOM 绑定阶段的逻辑 }, inserted(el, binding, vnode) { // 这里可以处理 DOM 插入阶段的逻辑 }, update(el, binding, vnode) { // 这里可以处理 DOM 更新阶段的逻辑 }, componentUpdated(el, binding, vnode) { // 这里可以处理组件更新阶段的逻辑 }, unbind(el, binding, vnode) { // 这里可以处理 DOM 解绑阶段的逻辑 } });
上述代码中,我们使用 Vue.directive() 方法来注册一个名为 my-directive 的自定义指令。该方法有五个回调函数 bind、inserted、update、componentUpdated、unbind (如上所述)分别在不同生命周期中执行相应的回调函数。
当我们在 HTML 页面中使用 my-directive 时(如
除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:
点击此处实现
当页面加载时,该元素将获得焦点 (注意:autofocus
在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。此外,你可以单击 Rerun
按钮,输入将被聚焦。
现在让我们用指令来实现这个功能:
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive("focus", {
// 当被绑定的元素插入到 DOM 中时……
mounted(el) {
// Focus the element
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus
property,如下:
<input v-focus />
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
beforeMount
:当指令第一次绑定到元素并且在挂载父组件之前调用。在这里你可以做一次性的初始化设置。mounted
:在挂载绑定元素的父组件时调用。beforeUpdate
:在更新包含组件的 VNode 之前调用。提示
我们会在稍后讨论渲染函数时介绍更多 VNodes 的细节。
updated
:在包含组件的 VNode 及其子组件的 VNode 更新后调用。beforeUnmount
:在卸载绑定元素的父组件之前调用unmounted
:当指令与元素解除绑定且父组件已卸载时,只调用一次。
接下来我们来看一下在自定义指令 API 钩子函数的参数 (即 el
、binding
、vnode
和 prevNnode
)
指令的参数可以是动态的。例如,在 v-mydirective:[argument]="value"
中,argument
参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。
例如你想要创建一个自定义指令,用来通过固定布局将元素固定在页面上。我们可以像这样创建一个通过指令值来更新竖直位置像素值的自定义指令:
<div id="dynamic-arguments-example" class="demo">
<p>Scroll down the page</p>
<p v-pin="200">Stick me 200px from the top of the page</p>
</div>
const app = Vue.createApp({})
app.directive("pin", {
mounted(el, binding) {
el.style.position = "fixed"
// binding.value is the value we pass to directive - in this case, it"s 200
el.style.top = binding.value + "px"
}
})
app.mount("#dynamic-arguments-example")
这会把该元素固定在距离页面顶部 200 像素的位置。但如果场景是我们需要把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。
<div id="dynamicexample">
<h3>Scroll down inside this section ↓</h3>
<p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
</div>
const app = Vue.createApp({
data() {
return {
direction: "right"
}
}
})
app.directive("pin", {
mounted(el, binding) {
el.style.position = "fixed"
// binding.arg is an argument we pass to directive
const s = binding.arg || "top"
el.style[s] = binding.value + "px"
}
})
app.mount("#dynamic-arguments-example")
结果:
点击此处实现
我们的定制指令现在已经足够灵活,可以支持一些不同的用例。为了使其更具动态性,我们还可以允许修改绑定值。让我们创建一个附加属性 pinPadding
,并将其绑定到 <input type="range">
。
<div id="dynamicexample">
<h2>Scroll down the page</h2>
<input type="range" min="0" max="500" v-model="pinPadding">
<p v-pin:[direction]="pinPadding">Stick me {{ pinPadding + "px" }} from the {{ direction }} of the page</p>
</div>
const app = Vue.createApp({
data() {
return {
direction: "right",
pinPadding: 200
}
}
})
让我们扩展我们的指令逻辑来重新计算固定元件更新的距离。
app.directive("pin", {
mounted(el, binding) {
el.style.position = "fixed"
const s = binding.arg || "top"
el.style[s] = binding.value + "px"
},
updated(el, binding) {
const s = binding.arg || "top"
el.style[s] = binding.value + "px"
}
})
结果:
点击此处实现
在很多时候,你可能想在 mounted
和 updated
时触发相同行为,而不关心其它的钩子。比如这样写:
app.directive("pin", (el, binding) => {
el.style.position = "fixed"
const s = binding.arg || "top"
el.style[s] = binding.value + "px"
})
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
<div v-demo="{ color: "white", text: "hello!" }"></div>
app.directive("demo", (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
在 3.0 中,有了片段支持,组件可能有多个根节点。如果在具有多个根节点的组件上使用自定义指令,则会产生问题。
要解释自定义指令如何在 3.0 中的组件上工作的详细信息,我们首先需要了解自定义指令在 3.0 中是如何编译的。对于这样的指令:
<div v-demo="test"></div>
将大概编译成:
const vDemo = resolveDirective("demo")
return withDirectives(h("div"), [[vDemo, test]])
其中 vDemo
是用户编写的指令对象,其中包含 mounted
和 updated
等钩子。
withDirectives
返回一个克隆的 VNode,其中用户钩子被包装并作为 VNode 生命周期钩子注入 (请参见渲染函数更多详情):
{
onVnodeMounted(vnode) {
// call vDemo.mounted(...)
}
}
因此,自定义指令作为 VNode 数据的一部分完全包含在内。当在组件上使用自定义指令时,这些 onVnodeXXX
钩子作为无关的 prop 传递给组件,并以 this.$attrs
结束。
这也意味着可以像这样在模板中直接挂接到元素的生命周期中,这在涉及到自定义指令时非常方便:
<div @vnodeMounted="myHook" />
这和 非 prop 的 attribute类似。因此,组件上自定义指令的规则将与其他无关 attribute 相同:由子组件决定在哪里以及是否应用它。当子组件在内部元素上使用 v-bind="$attrs"
时,它也将应用对其使用的任何自定义指令。
Vue 鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。...
插件是自包含的代码,通常向 Vue 添加全局级功能。它可以是公开 install() 方法的 object,也可以是 function插件的功能范围没有...
Web 可访问性 (也称为 a11y) 是指创建可供任何人使用的网站的实践方式——无论是身患某种障碍、通过慢速的网络连接访问、使用老...
#文档WCAG 2.0WCAG 2.1可访问的富 Internet 应用程序 (WAI-ARIA) 1.2WAI-ARIA 创作实践 1.2#辅助技术屏幕阅读器NVDAVoiceOver[JA...
#概览非兼容:自定义元素白名单现在在模板编译期间执行,应该通过编译器选项而不是运行时配置来配置。非兼容:特定 is prop 用法...