vuejs 教程,vue3.0 文档,vuejs 实战,前端框架,vuejs 入门,vue3.0 迁移,vue3资讯,Vue3最新动态,vuejs 中文社区,javascript中文网,vuejs 面试题,vuejs bug

# 自定义指令

# 简介

除了核心功能默认内置的指令 (v-modelv-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:

See the Pen Custom directives: basic example by Vue (@Vue) on CodePen.

当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。此外,你可以单击 Rerun 按钮,输入将被聚焦。

现在让我们用指令来实现这个功能:

const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  mounted(el) {
    // Focus the element
    el.focus()
  }
})
1
2
3
4
5
6
7
8
9

如果想注册局部指令,组件中也接受一个 directives 的选项:

directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}
1
2
3
4
5
6
7
8

然后你可以在模板中任何元素上使用新的 v-focus property,如下:

<input v-focus />
1

# 钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用。在这里你可以做一次性的初始化设置。

  • mounted:在挂载绑定元素的父组件时调用。

  • beforeUpdate:在更新包含组件的 VNode 之前调用。

提示

我们会在稍后讨论渲染函数时介绍更多 VNodes 的细节。

  • updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用。

  • beforeUnmount:在卸载绑定元素的父组件之前调用

  • unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次。

接下来我们来看一下在自定义指令 API 钩子函数的参数 (即 elbindingvnodeprevNnode)

# 动态指令参数

指令的参数可以是动态的。例如,在 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>
1
2
3
4
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')
1
2
3
4
5
6
7
8
9
10
11

这会把该元素固定在距离页面顶部 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>
1
2
3
4
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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

结果:

See the Pen Custom directives: dynamic arguments by Vue (@Vue) on CodePen.

我们的定制指令现在已经足够灵活,可以支持一些不同的用例。为了使其更具动态性,我们还可以允许修改绑定值。让我们创建一个附加属性 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>
1
2
3
4
5




 




const app = Vue.createApp({
  data() {
    return {
      direction: 'right',
      pinPadding: 200
    }
  }
})
1
2
3
4
5
6
7
8

让我们扩展我们的指令逻辑来重新计算固定元件更新的距离。







 
 
 
 


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'
  }
})
1
2
3
4
5
6
7
8
9
10
11

结果:

See the Pen Custom directives: dynamic arguments + dynamic binding by Vue (@Vue) on CodePen.

# 函数简写

在很多时候,你可能想在 mountedupdated 时触发相同行为,而不关心其它的钩子。比如这样写:

app.directive('pin', (el, binding) => {
  el.style.position = 'fixed'
  const s = binding.arg || 'top'
  el.style[s] = binding.value + 'px'
})
1
2
3
4
5

# 对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
1
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text) // => "hello!"
})
1
2
3
4

# 在组件中使用

When used on components, custom directive will always apply to component's root node, similarly to non-prop attributes.

<my-component v-demo="test"></my-component>
1
app.component('my-component', {
  template: `
    <div> // v-demo directive will be applied here
      <span>My component content</span>
    </div>
  `
})
1
2
3
4
5
6
7

Unlike attributes, directives can't be passed to a different element with v-bind="$attrs".

With fragments support, components can potentially have more than one root nodes. When applied to a multi-root component, directive will be ignored and the warning will be thrown.

Js中文网,专注分享前端最新技术、大厂面试题、聊点程序员轶事、职场感悟,做前端技术的传播者.

加入前端布道师交流群

扫描二维码回复 加群 学习,与大厂大佬讨论技术.