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 层次选择器siblings与nextAll的区别介绍
Aug 02 Javascript
js switch case default 的用法示例介绍
Oct 23 Javascript
JS按字节截取字符长度实例
Nov 20 Javascript
jQuery学习笔记之 Ajax操作篇(三) - 过程处理
Jun 23 Javascript
HTML,CSS,JavaScript速查表推荐
Dec 02 Javascript
jQuery圆形统计图开发实例
Jan 04 Javascript
使用AmplifyJS组件配合JavaScript进行编程的指南
Jul 28 Javascript
JS未跨域操作iframe里的DOM
Jun 01 Javascript
js实现背景图自适应窗口大小
Jan 10 Javascript
javascript系统时间设置操作示例
Jun 17 Javascript
JS实现扫码枪扫描二维码功能
Jan 03 Javascript
Javascript webpack动态import
Apr 19 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
mysql5的sql文件导入到mysql4的方法
2008/10/19 PHP
PHP生成条形图的方法
2014/12/10 PHP
PHP PDOStatement::errorInfo讲解
2019/01/31 PHP
javascript中apply和call方法的作用及区别说明
2014/02/14 Javascript
jquery选择器原理介绍($()使用方法)
2014/03/25 Javascript
js实现在同一窗口浏览图片
2014/09/17 Javascript
JavaScript字符串对象的concat方法实例(用于连接两个或多个字符串)
2014/10/16 Javascript
jQuery实现固定在网页顶部的菜单效果代码
2015/09/02 Javascript
JSON简介以及用法汇总
2016/02/21 Javascript
JavaScript SHA-256加密算法详细代码
2016/10/06 Javascript
JavaScript的事件机制详解
2017/01/17 Javascript
jquery使用EasyUI Tree异步加载JSON数据(生成树)
2017/02/11 Javascript
详解Vue2中组件间通信的解决全方案
2017/07/28 Javascript
JS基于正则表达式实现的密码强度验证功能示例
2017/09/21 Javascript
Vue表单demo v-model双向绑定问题
2018/06/29 Javascript
解决ant Design中this.props.form.validateFields未执行的问题
2020/10/27 Javascript
python中迭代器(iterator)用法实例分析
2015/04/29 Python
python计算文本文件行数的方法
2015/07/06 Python
如何用itertools解决无序排列组合的问题
2017/05/18 Python
Python正确重载运算符的方法示例详解
2017/08/27 Python
Python enumerate索引迭代代码解析
2018/01/19 Python
python实现决策树、随机森林的简单原理
2018/03/26 Python
python 读取DICOM头文件的实例
2018/05/07 Python
Django Form 实时从数据库中获取数据的操作方法
2019/07/25 Python
python实现简易学生信息管理系统
2020/04/05 Python
python 利用jieba.analyse进行 关键词提取
2020/12/17 Python
美国名表在线商城:Ashford(支持中文)
2019/09/24 全球购物
Java中的类包括什么内容?设计时要注意哪些方面
2012/05/23 面试题
在校生党员自我评价
2013/09/25 职场文书
农业大学毕业生的个人自我评价
2013/10/11 职场文书
淘宝网店营销策划书
2014/01/11 职场文书
药品促销活动方案
2014/02/14 职场文书
委托书怎么写
2014/07/31 职场文书
消防演习通知
2015/04/25 职场文书
springboot项目以jar包运行的操作方法
2021/06/30 Java/Android
Python序列化模块JSON与Pickle
2022/06/05 Python