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 相关文章推荐
JQuery获取元素文档大小、偏移和位置和滚动条位置的方法集合
Jan 12 Javascript
ASP.NET jQuery 实例4(复制TextBox的文本到本地剪贴板上)
Jan 13 Javascript
jQuery 过滤not()与filter()实例代码
May 10 Javascript
window.location 对象所包含的属性
Oct 10 Javascript
JS修改iframe页面背景颜色的方法
Apr 01 Javascript
基于Javascript实现弹出页面效果
Jan 01 Javascript
JavaScript中对JSON对象的基本操作示例
May 21 Javascript
JS组件系列之JS组件封装过程详解
Apr 28 Javascript
webpack 样式加载的实现原理
Jun 12 Javascript
JavaScript设计模式之代理模式简单实例教程
Jul 03 Javascript
基于vue3.0.1beta搭建仿京东的电商H5项目
May 06 Javascript
vue实现循环滚动列表
Jun 30 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对称加密算法示例
2014/05/07 PHP
ThinkPHP5框架实现简单的批量查询功能示例
2018/06/07 PHP
仿新浪微博登陆邮箱提示效果的js代码
2013/08/02 Javascript
jquery 实现窗口的最大化不论什么情况
2013/09/03 Javascript
javascript字母大小写转换的4个函数详解
2014/05/09 Javascript
js制作带有遮罩弹出层实现登录注册表单特效代码分享
2015/09/05 Javascript
js带缩略图的图片轮播效果代码分享
2015/09/14 Javascript
使用JQuery实现Ctrl+Enter提交表单的方法
2015/10/22 Javascript
js实现文件上传表单域美化特效
2015/11/02 Javascript
JS实现按比例缩放图片的方法(附C#版代码)
2015/12/08 Javascript
js解决movebox移动问题
2016/03/29 Javascript
Angularjs结合Bootstrap制作的一个TODO List
2016/08/18 Javascript
Javascript 创建类并动态添加属性及方法的简单实现
2016/10/20 Javascript
jquery UI Datepicker时间控件冲突问题解决
2016/12/16 Javascript
基于jQuery实现顶部导航栏功能
2016/12/27 Javascript
javaScript canvas实现(画笔大小 颜色 橡皮的实例)
2017/11/28 Javascript
nodejs提示:cross-device link not permitted, rename错误的解决方法
2019/06/10 NodeJs
通过Nodejs搭建网站简单实现注册登录流程
2019/06/14 NodeJs
解决nuxt 自定义全局方法,全局属性,全局变量的问题
2020/11/05 Javascript
详解vue修改elementUI的分页组件视图没更新问题
2020/11/13 Javascript
python利用hook技术破解https的实例代码
2013/03/25 Python
Python二分法搜索算法实例分析
2015/05/11 Python
举例详解Python中yield生成器的用法
2015/08/05 Python
Python及Django框架生成二维码的方法分析
2018/01/31 Python
Scrapy框架爬取Boss直聘网Python职位信息的源码
2019/02/22 Python
PyQt5 对图片进行缩放的实例
2019/06/18 Python
Python 中的参数传递、返回值、浅拷贝、深拷贝
2019/06/25 Python
详解numpy矩阵的创建与数据类型
2019/10/18 Python
opencv+python实现均值滤波
2020/02/19 Python
Python3批量创建Crowd用户并分配组
2020/05/20 Python
Python如何对齐字符串
2020/07/30 Python
pycharm激活码免费分享适用最新pycharm2020.2.3永久激活
2020/11/25 Python
Farfetch香港官网:汇集全球时尚奢侈品购物平台
2017/11/26 全球购物
zooplus德国:便宜地订购动物用品、动物饲料、动物食品
2020/05/06 全球购物
小学生我的梦想演讲稿
2014/08/21 职场文书
详解python中[-1]、[:-1]、[::-1]、[n::-1]使用方法
2021/04/25 Python