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的简单&amp;简陋Tabs插件代码
Feb 09 Javascript
jquery手风琴特效插件
Feb 04 Javascript
jquery实现用户信息修改验证输入方法汇总
Jul 18 Javascript
javascript常用的方法整理
Aug 20 Javascript
Google Maps基础及实例解析
Aug 06 Javascript
js控制台输出的方法(详解)
Nov 26 Javascript
JavaScript实现重力下落与弹性效果的方法分析
Dec 20 Javascript
JavaScript EventEmitter 背后的秘密 完整版
Mar 29 Javascript
浅谈Angular HttpClient简单入门
May 04 Javascript
layui自定义工具栏的方法
Sep 19 Javascript
微信小程序如何实现在线客服功能
Oct 16 Javascript
在vue中使用echarts(折线图的demo,markline用法)
Jul 20 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 判断访客是否为搜索引擎蜘蛛的函数代码
2011/07/29 PHP
php适配器模式介绍
2012/08/14 PHP
php.ini 配置文件的深入解析
2013/06/17 PHP
php5.3 goto函数介绍和示例
2014/03/21 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
浅谈thinkphp的实例化模型
2015/01/04 PHP
基于Web标准的UI组件 — 树状菜单(2)
2006/09/18 Javascript
在chrome浏览器中,防止input[text]和textarea在聚焦时出现黄色边框的解决方法
2011/05/24 Javascript
node.js中的fs.unlinkSync方法使用说明
2014/12/15 Javascript
深入学习jQuery Validate表单验证(二)
2016/01/18 Javascript
jquery获取文档高度和窗口高度汇总
2016/01/25 Javascript
js获取页面及个元素高度、宽度的代码
2016/04/26 Javascript
浅析Javascript的自动分号插入(ASI)机制
2016/09/29 Javascript
javascript设计模式之模块模式学习笔记
2017/02/15 Javascript
Vue插件写、用详解(附demo)
2017/03/20 Javascript
响应式框架Bootstrap栅格系统的实例
2017/12/19 Javascript
微信小程序实现全国机场索引列表
2018/01/31 Javascript
react组件基本用法示例小结
2020/04/27 Javascript
Python使用metaclass实现Singleton模式的方法
2015/05/05 Python
Python中remove漏删和索引越界问题的解决
2020/03/18 Python
如何用Python绘制3D柱形图
2020/09/16 Python
H5 canvas实现贪吃蛇小游戏
2017/07/28 HTML / CSS
Foot Locker意大利官网:全球领先的运动鞋和服装零售商
2017/05/30 全球购物
梅西百货澳大利亚:Macy’s Australia
2017/07/26 全球购物
德国BA保镖药房中文网:Bodyguard Apotheke
2021/03/09 全球购物
浅谈react路由传参的几种方式
2021/03/23 Javascript
行政助理求职自荐信
2013/10/26 职场文书
客服文员岗位职责
2013/11/29 职场文书
医学专业本科毕业生自我鉴定
2013/12/28 职场文书
音乐教育感言
2014/03/05 职场文书
产品质量保证书
2014/04/29 职场文书
2014年技术员工作总结
2014/11/18 职场文书
培训通知
2015/04/17 职场文书
开发一个封装iframe的vue组件
2021/03/29 Vue.js
Pytorch 统计模型参数量的操作 param.numel()
2021/05/13 Python
Django REST framework 限流功能的使用
2021/06/24 Python