课程大纲
- (1)全局API
- (2)自定义指令—起源
- (3)自定义指令—分类
- (4)自定义指令—注册
- (5)自定义指令—调用
- (6)自定义指令—生命周期钩子
全局API
- (1)什么是全局API?
全局API并不在构造器里,而是先声明全局变量或者直接在Vue上定义一些新功能,Vue内置了一些全局API,比如本节要介绍的指令Vue.directive
通俗理解:就是在构造器外部用Vue提供的API函数来定义新的功能。 - (2)常用vue 的全局 API列表
1、Vue.directive 自定义指令
2、Vue.extend 扩展实例构造器
3、全局操作Vue.set + Vue.delete
4、Vue 的生命周期
5、Vue component 组件 + Vue template模板
6、Vue.nextTick线程操作、Vue.filter筛选、Vue.use调用
小结:全局API就是在构造器外部用Vue提供的API函数来定义新的功能。
自定义指令起源
- (2)缘由:
Vue2.0开始代码复用性和抽象的主要形式是组件,然而个别情况下仍然需要对普通DOM元素进行底层操作。
之前所学的v-if、v-show等都是vue内置指令,有时部分功能是无法通过内置指令实现,此时便需要用到自定义指令。 - (3)场景:
页面加载完毕后要求输入框自定聚焦,这种功能在很多场景都会用到,例如百度、京东、淘宝等
自定义指令分类
- (4)自定义指令分类
自定义指令分为全局和局部,正式介绍自定义指令之前,先来回顾下组件的相关知识
组件分类:全局组件和局部组件,语法如下:
全局组件注册语法为Vue.component()
局部组件注册需要借助components选项
自定义指令注册
- (5)注册自定义指令
同理,自定义指令注册语法与之类似,如下所示:
全局自定义指令注册语法为Vue.directive()
局部自定义指令注册需要借助directives选项
全局指令VS局部指令
- 自定义指令分为全局+局部
全局注册为Vue.directive()
局部注册为directives选项内注册 - 区别:
作用域不同,全局注册的自定义指令可以在全局调用,局部注册的自定义指令只能在当前构造器作用域内使用(类似于全局组件与局部组件)。
自定义指令调用
-
(6)自定义指令调用
注册完毕后,在在需要使用的组件里进行调用
在介绍自定义指令调用前,先来回顾下组件的调用
-
上图对比两者,因为HTML对英文大小写不敏感,所以在注册时使用驼峰命名法,在调用时使用短横线分割法调用。
-上图 总结:
注册时→驼峰法;调用时→短横线法; -
(6)自定义指令调用
注册完毕后,在在需要使用的组件里进行调用,语法v-directive-name
调用自定义指令时,语法类似调用组件,如下所示
-
上图总结:
注册时→驼峰法;
调用时→短横线法;自定义指令
-
案例:
利用自定义指令,实现一个输入框自动聚焦功能,代码如下
自定义指令钩子/生命周期
- 上例只是注册了自定义指令v-auto-focus,还没有实现具体功能,要想实现自动聚焦功能,需要结合选项去实现。下面具体介绍自定义指令的各个选项。 自定义指令的选项是由几个生命周期钩子函数组成的,每个都是可选的。
- 常见生命周期钩子如下
1、bind第一次绑定到元素时调用
2、inserted被绑定元素插入父节点时调用
3、update被绑定元素所在的模板更新时调用
4、componentUpdated被绑定元素所在模板完成一次更新周期时调用
5、unbind指令与元素解绑时调用 - (7-1)bind初始化钩子
含义:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。验证如下
- (7-2)inserted绑定钩子
含义:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)
(7-3)update更新钩子
含义:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新
- (7-4)componentUpdated模板更新完毕钩子
含义:被绑定元素所在模板完成一次更新周期时调用,即所在组件的 vnode 及其孩子的 vnode 全部更新时调用
- (7-5)unbind解绑钩子
含义:指令只调用一次,指令与元素解绑时调用
测试:刷新页面后执行bind与inserted钩子,更新时执行update和componentUpdated钩子,接下来添加解绑操作,看看会不会触发unbind解绑钩子… … - 添加按钮,点击实现vue实例解绑
销毁实例绑定,即可实现解绑
注意:销毁的不是DOM元素,而是该vue实例相关的方法和属性,也就是说以后再更新元素时,也不会触发update和componentUpdated更新等相关的生命周期钩子 - (7-5)unbind解绑钩子
自定义指令
- (8)自动聚焦案例
目前为止,常用自定义指令相关的生命周期钩子已经介绍完毕,但仍然未实现输入框自动聚焦功能。 - 该功能还需要一些钩子函数参数el、binding、vnode,参数解析:
①el:指令所绑定的元素,可以用来直接操作DOM
②binding: 一个对象,包含指令的很多信息
③vnode: Vue编译生成的虚拟节点virtual node
自定义指令参数
- (9)钩子函数参数
接下来介绍下钩子函数相关参数,验证如下
随意选择一个生命周期,在里面测试即可
- (9)钩子函数参数
结合控制台输出结果可以分析出el为绑定元素、binding为一个对象(包含指令相关信息)、vnode为虚拟节点
所以如果想操作DOM,可以利用el - (10)自动聚焦案例
接下来结合生命周期钩子和相关参数el实现聚焦功能 - 分析:
页面加载完毕后触发bind和inserted,但DOM元素聚焦操作需要等待被绑定元素插入父节点后才能执行,所以将聚焦功能写到inserted绑定钩子里即可,如下所示
- (11)钩子函数参数binding对象详解
接下来详细介绍下自定义指令钩子函数的参数binding对象 - binding: 一个对象,包含指令的很多信息
1、name:指令名,不包括v-前缀。
2、value:指令的绑定值,例:v-my-directive=“1 + 1”, value 的值是2。
3、oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。
4、expression:绑定值的字符串形式。例如v-my-directive=“1 + 1”,expression 的值是"1 + 1"
5、arg:传给指令的参数。例如v-my-directive:foo,arg 的值是"foo"。
6、modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar, 修饰符对象 modifiers 的值是{ foo: true, bar: true }。
7、vnode:Vue 编译生成的虚拟节点,查阅 VNode API 了解更多详情。
8、oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。
自定义指令-钩子案例
- (12-1)bind初始化钩子之变色案例
接下来再做个变色的自定义指令v-color,要求绑定该自定义指令的元素字体颜色变红,如下所示
自定义指令-聚焦+变色代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
<style type="text/css">
</style>
<link rel="stylesheet" type="text/css" href="../css/animate.min.css" />
<script src="../js/velocity.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/vue-2.6.9.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="root">
<input type="text" v-auto-focus v-color-red="color" v-model="name">
<button @click="destroyVue">销毁</button>
</div>
<script type="text/javascript">
/*注册全局自定义指令*/
Vue.directive('autoFocus', {
/*选项*/
bind(el, binding, vnode) {
console.log('bind-第一次绑定到元素时调用')
console.log(el)
console.log(binding)
console.log(vnode)
},
inserted(el) {
console.log('inserted-被绑定元素插入父节点时调用')
el.focus();
},
update() {
console.log('update-被绑定元素所在的模板更新时调用')
},
componentUpdated() {
console.log('componentUpdated-模板更新完毕钩子')
},
unbind() {
console.log('unbind-解绑时调用')
}
})
Vue.directive('colorRed', {
bind(el, binding) {
el.style = "color:" + binding.value
console.log(binding)
}
})
var root = new Vue({
el: '#root',
data: {
name: '',
color: 'red'
},
methods: {
destroyVue() {
this.$destroy(); /*销毁Vue实例*/
}
}
/*
局部注册自定义指令
directives:{
'autoFocus':{
}
}
*/
})
</script>
</body>
</html>
自定义指令-钩子案例
- (12-2)inserted绑定钩子之图片墙案例(练习题)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义指令</title>
<script src="../js/vue-2.6.9.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/velocity.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
.box {
width: 800px;
height: 800px;
position: relative;
background-color: #000000;
margin: auto;
}
.img {
width: 200px;
position: absolute;
left: 0;
top: 0;
transform: rotateZ(0deg);
}
</style>
</head>
<body>
<div id="root">
<div class="box">
<img src="../img/t1.png" class="img" v-ball>
<img src="../img/t2.png" class="img" v-ball>
<img src="../img/t3.png" class="img" v-ball>
<img src="../img/t4.png" class="img" v-ball>
<img src="../img/t5.png" class="img" v-ball>
<img src="../img/t6.png" class="img" v-ball>
<img src="../img/t7.png" class="img" v-ball>
</div>
</div>
<script type="text/javascript">
/* 1、注册 */
Vue.directive('ball', {
inserted: function(el) {
var i = 0;
el.onclick = function(e) {
i += 10;
el.style.transform = "rotateZ(" + i + "deg)"
};
el.onmousedown = function(e) {
var l = e.clientX - el.offsetLeft;
var t = e.clientY - el.offsetTop;
document.onmousemove = function(e) {
el.style.left = (e.clientX - l) + 'px';
el.style.top = (e.clientY - t) + 'px'
};
el.onmouseup = function() {
document.onmousemove = null;
el.onmouseup = null;
}
}
}
});
var root = new Vue({
el: '#root'
})
</script>
</body>
</html>
- (12-3)update更新钩子之随机色案例(练习题)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义指令</title>
<script src="../js/velocity.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/vue-2.6.9.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
#text {
width: 100px;
height: 100px;
resize: none;
}
</style>
</head>
<body>
<div id="root">
<textarea id="text" v-model="inputValue" v-box></textarea>
</div>
<script type="text/javascript">
/* 1、注册 */
Vue.directive('box', {
update: function(el) {
let color1 = Math.ceil(Math.random() * 225);
let color2 = Math.ceil(Math.random() * 225);
let color3 = Math.ceil(Math.random() * 225);
el.style.backgroundColor = 'rgb(' + color1 + "," + color2 + ',' + color3 + ")"
}
});
var root = new Vue({
el: '#root',
data: {
inputValue: ''
}
})
</script>
</body>
</html>
- (12-4)bind初始化钩子之下拉列表(练习题)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>自定义指令</title>
<script src="../js/velocity.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/vue-2.6.9.min.js" type="text/javascript" charset="utf-8"></script>
<style type="text/css">
[v-cloak] {
display: none;
}
.main {
width: 125px;
}
button {
display: block;
width: 100%;
color: #fff;
background-color: #39f;
border: 0;
padding: 6px;
text-align: center;
font-size: 12px;
border-radius: 4px;
cursor: pointer;
outline: none;
position: relative;
}
button {
top: 1px;
left: 1px;
}
.dropdown {
width: 100%;
height: 150px;
margin: 5px 0;
font-size: 12px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
}
.dropdown p {
display: inline-block;
padding: 6px;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div class="main" v-click-outside="handleClose">
<button @click="show =!show">点击显示下拉菜单</button>
<div class="dropdown" v-show="show">
<p>下拉框的内容,点击外部区域可以关闭</p>
</div>
</div>
</div>
<script type="text/javascript">
/* 1、注册 */
Vue.directive('clickOutside', {
bind: function(el, binding) {
console.log(binding)
function documentHandler(e) {
//判断点击的区域是否是指令所在的元素内部,如果是,就跳出函数,不往下执行。
if (el.contains(e.target)) {
return false;
}
//判断当前的指令v-clickoutside有没有写表达式
if (binding.expression) {
//binding.value()就是用来执行当前上下文methods中指定的函数的
binding.value(e);
}
}
//用于在unbind钩子函数中移除对document的click事件监听。
el.__vueClickOutside__ = documentHandler;
document.addEventListener('click', documentHandler);
},
unbind: function() {
document.removeEventListener('click', el.__vueClickOutside__);
//如果不移除,当组件或元素销毁时,它仍存在于内存中
delete el.__vueClickOutside__;
}
})
var app = new Vue({
el: '#app',
data: {
show: false
},
methods: {
handleClose() {
this.show = false;
}
}
})
/*
要在document上绑定click事件,所以在bind钩子内声明了一个函数documentHandler
并将它作为句柄绑定在document的click事件上。documentHandler函数做了两个判断
第一个是判断点击的区域是否是指令所在的元素内部,如果是,就跳出函数,不往下继续执行。
contains方法是用来判断元素A是否包含了元素B,包含返回true,不包含返回false,示例代码如下:
<div id="parent">
父元素
<div id="children">子元素</div>
</div>
var A =document.getElementById('parent');
var B =document.getElementById('children');
console.log(A.contains(B));//true
console.log(B.contains(A));//false
*/
</script>
</body>
</html>