Vue.js每天必学之指令系统与自定义指令


Posted in Javascript onSeptember 07, 2016

基础

除了内置指令,Vue.js 也允许注册自定义指令。自定义指令提供一种机制将数据的变化映射为 DOM 行为。

可以用 Vue.directive(id, definition) 方法注册一个全局自定义指令,它接收两个参数指令 ID 与定义对象。也可以用组件的 directives 选项注册一个局部自定义指令。

钩子函数

定义对象可以提供几个钩子函数(都是可选的):

 •bind:只调用一次,在指令第一次绑定到元素上时调用。

 •update: 在 bind 之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值。

 •unbind:只调用一次,在指令从元素上解绑时调用。

 示例

Vue.directive('my-directive', {
 bind: function () {
  // 准备工作
  // 例如,添加事件处理器或只需要运行一次的高耗任务
 },
 update: function (newValue, oldValue) {
  // 值更新时的工作
  // 也会以初始值为参数调用一次
 },
 unbind: function () {
  // 清理工作
  // 例如,删除 bind() 添加的事件监听器
 }
})

在注册之后,便可以在 Vue.js 模板中这样用(记着添加前缀 v-):

<div v-my-directive="someValue"></div>

当只需要 update 函数时,可以传入一个函数替代定义对象:

Vue.directive('my-directive', function (value) {
 // 这个函数用作 update()
})

指令实例属性

所有的钩子函数将被复制到实际的指令对象中,钩子内 this 指向这个指令对象。这个对象暴露了一些有用的属性:
 •el: 指令绑定的元素。
 •vm: 拥有该指令的上下文 ViewModel。
 •expression: 指令的表达式,不包括参数和过滤器。
 •arg: 指令的参数。
 •name: 指令的名字,不包含前缀。
 •modifiers: 一个对象,包含指令的修饰符。
 •descriptor: 一个对象,包含指令的解析结果。 

你应当将这些属性视为只读的,不要修改它们。你也可以给指令对象添加自定义属性,但是注意不要覆盖已有的内部属性。

示例:

<div id="demo" v-demo:hello.a.b="msg"></div>

Vue.directive('demo', {
 bind: function () {
  console.log('demo bound!')
 },
 update: function (value) {
  this.el.innerHTML =
   'name - '    + this.name + '<br>' +
   'expression - ' + this.expression + '<br>' +
   'argument - '  + this.arg + '<br>' +
   'modifiers - ' + JSON.stringify(this.modifiers) + '<br>' +
   'value - '   + value
 }
})
var demo = new Vue({
 el: '#demo',
 data: {
  msg: 'hello!'
 }
})

对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令可以使用任意合法的 JavaScript 表达式:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>

Vue.directive('demo', function (value) {
 console.log(value.color) // "white"
 console.log(value.text) // "hello!"
})

字面修饰符

当指令使用了字面修饰符,它的值将按普通字符串处理并传递给 update 方法。update 方法将只调用一次,因为普通字符串不能响应数据变化。

<div v-demo.literal="foo bar baz">

Vue.directive('demo', function (value) {
 console.log(value) // "foo bar baz"
})

元素指令

有时我们想以自定义元素的形式使用指令,而不是以特性的形式。这与 Angular 的 “E” 指令非常相似。元素指令可以看做是一个轻量组件。可以像下面这样注册一个自定义元素指令:

Vue.elementDirective('my-directive', {
 // API 同普通指令
 bind: function () {
  // 操作 this.el...
 }
})

不这样写:

<div v-my-directive></div>

这样写:

<my-directive></my-directive>

元素指令不能接受参数或表达式,但是它可以读取元素的特性从而决定它的行为。

迥异于普通指令,元素指令是终结性的,这意味着,一旦 Vue 遇到一个元素指令,它将跳过该元素及其子元素——只有该元素指令本身可以操作该元素及其子元素。

高级选项

params

自定义指令可以接收一个 params 数组,指定一个特性列表,Vue 编译器将自动提取绑定元素的这些特性。例如:

<div v-example a="hi"></div>

Vue.directive('example', {
 params: ['a'],
 bind: function () {
  console.log(this.params.a) // -> "hi"
 }
})

此 API 也支持动态属性。this.params[key] 会自动保持更新。另外,可以指定一个回调,在值变化时调用:

<div v-example v-bind:a="someValue"></div>

Vue.directive('example', {
 params: ['a'],
 paramWatchers: {
  a: function (val, oldVal) {
   console.log('a changed!')
  }
 }
})

类似于 props,指令参数的名字在 JavaScript 中使用 camelCase 风格,在 HTML 中对应使用 kebab-case 风格。例如,假设在模板里有一个参数 `disable-effect`,在 JavaScript 里以 `disableEffect` 访问它。

deep

如果自定义指令用在一个对象上,当对象内部属性变化时要触发 update,则在指令定义对象中指定 deep: true。

<div v-my-directive="obj"></div>

Vue.directive('my-directive', {
 deep: true,
 update: function (obj) {
  // 在 `obj` 的嵌套属性变化时调用
 }
})

twoWay

如果指令想向 Vue 实例写回数据,则在指令定义对象中指定 twoWay: true 。该选项允许在指令中使用 this.set(value):

Vue.directive('example', {
 twoWay: true,
 bind: function () {
  this.handler = function () {
   // 将数据写回 vm
   // 如果指令这样绑定 v-example="a.b.c"
   // 它将用给定值设置 `vm.a.b.c`
   this.set(this.el.value)
  }.bind(this)
  this.el.addEventListener('input', this.handler)
 },
 unbind: function () {
  this.el.removeEventListener('input', this.handler)
 }
})

acceptStatement

传入 acceptStatement:true 可以让自定义指令接受内联语句,就像 v-on 那样:

<div v-my-directive="a++"></div>

Vue.directive('my-directive', {
 acceptStatement: true,
 update: function (fn) {
  // 传入值是一个函数
  // 在调用它时将在所属实例作用域内计算 "a++" 语句
 }
})

明智地使用,因为通常你要在模板中避免副效应。

terminal

1.0.19+ 

Vue 通过递归遍历 DOM 树来编译模块。但是当它遇到 terminal 指令时会停止遍历这个元素的后代元素。这个指令将接管编译这个元素及其后代元素的任务。v-if 和 v-for 都是 terminal 指令。

编写自定义 terminal 指令是一个高级话题,需要较好的理解 Vue 的编译流程,但这不是说不可能编写自定义 terminal 指令。用 terminal: true 指定自定义 terminal 指令,可能还需要用 Vue.FragmentFactory 来编译 partial。下面是一个自定义 terminal 指令,它编译它的内容模板并将结果注入到页面的另一个地方:

var FragmentFactory = Vue.FragmentFactory
var remove = Vue.util.remove
var createAnchor = Vue.util.createAnchor

Vue.directive('inject', {
 terminal: true,
 bind: function () {
  var container = document.getElementById(this.arg)
  this.anchor = createAnchor('v-inject')
  container.appendChild(this.anchor)
  remove(this.el)
  var factory = new FragmentFactory(this.vm, this.el)
  this.frag = factory.create(this._host, this._scope, this._frag)
  this.frag.before(this.anchor)
 },
 unbind: function () {
  this.frag.remove()
  remove(this.anchor)
 }
})
<div id="modal"></div>
...
<div v-inject:modal>
 <h1>header</h1>
 <p>body</p>
 <p>footer</p>
</div>

如果你想编写自定义 terminal 指令,建议你通读内置 terminal 指令的源码,如 v-if 和 v-for,以便更好地了解 Vue 的内部机制。

priority

可以给指令指定一个优先级。如果没有指定,普通指令默认是 1000, terminal 指令默认是 2000。同一个元素上优先级高的指令会比其它指令处理得早一些。优先级一样的指令按照它在元素特性列表中出现的顺序依次处理,但是不能保证这个顺序在不同的浏览器中是一致的。

可以在 API 中查看内置指令的优先级。另外,流程控制指令 v-if 和 v-for 在编译过程中始终拥有最高的优先级。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
一个加载js文件的小脚本
Jun 28 Javascript
javascript中的toFixed固定小数位数 简单实例分享
Jul 12 Javascript
jquery validate 自定义验证方法介绍 日期验证
Feb 27 Javascript
jquery实现submit提交表单
Feb 03 Javascript
Javascript验证方法大全
Sep 21 Javascript
JS 动态判断PC和手机浏览器实现代码
Sep 21 Javascript
详谈jQuery中使用attr(), prop(), val()获取value的异同
Apr 25 jQuery
提高Node.js性能的应用技巧分享
Aug 10 Javascript
巧妙运用v-model实现父子组件传值的方法示例
Apr 07 Javascript
Vue基于iview实现登录密码的显示与隐藏功能
Mar 06 Javascript
vue 实现超长文本截取,悬浮框提示
Jul 29 Javascript
vant中的toast层级改变操作
Nov 04 Javascript
Vue.js每天必学之过滤器与自定义过滤器
Sep 07 #Javascript
详解AngularJS中ng-src指令的使用
Sep 07 #Javascript
AngularJS实现按钮提示与点击变色效果
Sep 07 #Javascript
JS实现简单易用的手机端浮动窗口显示效果
Sep 07 #Javascript
利用Angularjs实现幻灯片效果
Sep 07 #Javascript
轻松掌握JavaScript状态模式
Sep 07 #Javascript
JS简单实现tab切换效果的多窗口显示功能
Sep 07 #Javascript
You might like
PHP使用GIFEncoder类处理gif图片实例
2014/07/01 PHP
非常重要的php正则表达式详解
2016/01/04 PHP
Centos7 Yum安装PHP7.2流程教程详解
2019/07/02 PHP
js下写一个事件队列操作函数
2010/07/19 Javascript
Javascript生成json的函数代码(可以用php的json_decode解码)
2012/06/11 Javascript
js实现支持手机滑动切换的轮播图片效果实例
2015/04/29 Javascript
简介JavaScript中search()方法的使用
2015/06/06 Javascript
详解JavaScript节流函数中的Throttle
2016/07/16 Javascript
jQuery实现点击表格单元格就可以编辑内容的方法【测试可用】
2016/08/01 Javascript
浅谈angular懒加载的一些坑
2016/08/20 Javascript
JavaScript触发onScroll事件的函数节流详解
2016/12/14 Javascript
js处理层级数据结构的方法小结
2017/01/17 Javascript
JS+CSS实现网页加载中的动画效果
2017/10/27 Javascript
JavaScript页面倒计时功能完整示例
2019/05/15 Javascript
vue中的过滤器实例代码详解
2019/06/06 Javascript
解决vue watch数据的方法被调用了两次的问题
2020/11/07 Javascript
[06:13]DOTA2进化论(修改版)
2013/10/08 DOTA
[03:33]TI9战队采访 - Infamous
2019/08/20 DOTA
Python脚本判断 Linux 是否运行在虚拟机上
2015/04/25 Python
Python中列表和元组的使用方法和区别详解
2020/12/30 Python
Python3使用turtle绘制超立方体图形示例
2018/06/19 Python
使用Python的Dataframe取两列时间值相差一年的所有行方法
2018/07/10 Python
在Python中,不用while和for循环遍历列表的实例
2019/02/20 Python
Python 网络编程之TCP客户端/服务端功能示例【基于socket套接字】
2019/10/12 Python
Python使用指定字符长度切分数据示例
2019/12/05 Python
H5调用相机拍照并压缩图片的实例代码
2017/07/20 HTML / CSS
在线吉他课程,学习如何弹吉他:Fender Play
2019/02/28 全球购物
捷克街头、运动和滑板一站式商店:BoardStar.cz
2019/10/06 全球购物
Shell编程面试题
2012/05/30 面试题
人力资源管理专业毕业生自我评价
2013/09/21 职场文书
网络工程专业毕业生推荐信
2013/10/28 职场文书
2014厂务公开实施方案
2014/02/17 职场文书
远程网络教育毕业生自我鉴定
2014/04/14 职场文书
加强作风建设演讲稿
2014/10/24 职场文书
小型婚礼主持词
2015/06/30 职场文书
Python matplotlib 利用随机函数生成变化图形
2022/04/26 Python