自定义指令分为全局指令 和局部指令
自定义指令
官方api 文档里有这么一句话:对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
1.全局指令
举例1:让指定文本框自动获取焦点
如果我们想实现这个例子,原生js的写法是:
//原生js写法:网页一打开,就让指定的输入框自动获取焦点
document.getElementById('search').focus()
但我们不建议这样做。我们可以通过Vue中的自定义指令来实现这个例子。
1.1 定义
所谓全局指令,是指可以在多个vue实例中使用的指令。
注册全局指令时,指令名不要用大写字母,且不要加v-,但是使用时一定要加前缀v-。格式如下:
Vue.directive('指令名',{定义对象})
定义对象中有5个钩子函数:bind,inserted,update,componentUpdated,unbind。
(1)使用Vue.directive()自定义全局指令:
//自定义全局指令 v-focus:让文本框自动获取焦点
//参数1:指令的名称。注意,在定义的时候,指令的名称前面,不需要加 v- 前缀;但是:在`调用`的时候,必须在指令名称前 加上 v- 前缀
//参数2:是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus', {
//在每个函数中,第一个参数,永远是 el ,表示被绑定了指令的那个 dom 元素,这个el 参数,是一个原生的 JS 对象,所以 Vue 自定义指令可以用来直接和 DOM 打交道
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用, 因为,一个元素,只有插入DOM之后,才能获取焦点
// el.focus()
},
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
el.focus()
// 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
},
updated: function (el) { // 当VNode更新的时候,会执行 updated, 【可能会触发多次】
}
})
上方的代码中,如果我们把el.focus()这行代码写在bind方法里,是没有效果的(但不会报错)。没有效果是因为,在执行到bind方法的时候,元素还没有插入到dom中去。
由此可以看看出:bind、inserted、updated这三个钩子函数的执行时机不同,且执行的次数有区别。
(2)在指定的文本框上加v-指令名:
<input type="text" id="search" v-model="keywords" v-focus>
1.2 传参
举例2:设置DOM元素的color样式
参考举例1中的写法,我们可能会这样给DOM元素设置样式:
<div id="app">
搜索框:
<input type="text" id="search" v-model="name" v-color>
</div>
<script>
//自定义全局指令 v-color:设置DOM元素的color属性
Vue.directive('color', {
bind: function (el) {
el.style.color = 'red';
},
})
new Vue({
el: '#app',
data: {
name: ''
}
})
</script>
如上方代码所示,我们自定义了一个指令v-color,然后在input标签中用上了这个指令,就给元素设置了color属性。但是这个代码有个弊端是:color的属性值在定义指令的时候,被写死了。如何完善呢?我们可以在DOM元素中传参。
//自定义全局指令 v-color:设置DOM元素的color属性
Vue.directive('color', {
// 只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
// 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
// 意思是说,我们可以把样式的代码写到bind中去(即使这个时候,dom元素还没有被创建)
bind: function (el, binding) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,【只执行一次】
console.log(binding.name); //打印结果:color
console.log(binding.value); //打印结果:green
el.style.color = binding.value// 通过bining拿到v-color中传递过来的值
},
})
上方代码中,bind方法里传递的第二个参数binding,可以拿到DOM元素中v-color里填的值。注意,v-color="‘green’",这里面写的是字符串常量;如果去掉单引号,就成了变量,不是我们想要的。
1.3 简写格式
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如上面的代码中,我们可以写成简写形式:
Vue.directive('color', function (el, binding) { //注意,这个function等同于把代码写到了 bind 和 update 中去
el.style.color = binding.value
})
2.私有指令
2.1 定义方式
自定义私有指令:在某一个 vue 对象内部自定义的指令称之为私有指令。这种指令只有在当前vue对象的el指定的监管区域有用。
代码举例:(设置文字的font-weight属性)
<div id="app">
<span v-fontweight="600">hello,word</span>
</div>
<script>
new Vue({
el: '#app',
//自定义私有指令
directives: {
'fontweight': {
bind: function (el, binding) {
el.style.fontWeight = binding.value;
}
}
}
})
</script>
注意, el.style.fontWeight设置属性值,至少要600,否则看不到加粗的效果。
2.2 简写格式
//自定义私有指令(简写形式)
directives: {
'fontweight': function (el, binding) { //注意,这个function等同于把代码写到了 bind 和 update 中去
el.style.fontWeight = binding.value;
}
}
3.钩子函数
钩子函数说白了也就是生命周期,即当一个指令绑定到一个元素上时,这个指令内部有5个生命周期事件函数。接下来创建一个案例来看看这几个钩子函数的触发情况:
Vue 提供了自定义指令的5个钩子函数:
- bind:当指令绑定在 HTML 元素上时触发,只执行一次。在这里可以进行一次性的初始化设置
- inserted:被绑定的元素,插入到父节点的 DOM 中时调用(仅保证父节点存在)。
- update:组件更新时调用。当指令绑定的元素状态/样式、内容(这里指元素绑定的 vue 数据) 发生改变时触发
- componentUpdated:组件与子组件更新时调用。
- unbind:当指令绑定的元素从 dom 中删除时触发,只执行一次。
案例:优化图片加载
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="vue.js"></script>
<style>
img {
width: 275px;
height: 152px;
}
</style>
</head>
<body>
<div id="app">
<!-- //先不要设置src的属性 -->
<img v-imgload="item" v-for="item in imgList">
<!-- <img :src="item" alt="" v-for="(item,index) in imgList"> -->
</div>
<script>
Vue.directive("imgload", (el, binding) => {
//先设置元素的背景色
el.style.backgroundColor = "#ff6700";
// el.src = "holder.js/275x152";
//等待图片加载完成后,修改图片的地址
var img = new Image();
//用创建好的img对象 去加载图片
img.src = binding.value;
//用创建好的img对象,去加载图片
img.onload = () => {
// el.style.backgroundColor = "";
//只要图片加载过,再次请求图片的时候,就会直接从缓存中获取。
el.src = binding.value;
}
})
var vm = new Vue({
el: "#app",
data: {
imgList: ["https://pic.3gbizhi.com/2021/1227/20211227082808899.jpg.278.154.jpg", "https://pic.3gbizhi.com/2021/1203/20211203075609486.jpg.278.154.jpg", "https://pic.3gbizhi.com/2021/1203/20211203075604804.jpg.278.154.jpg", "https://pic.3gbizhi.com/2021/1203/20211203075607171.jpg.278.154.jpg", "https://pic.3gbizhi.com/2021/1208/20211208022355980.jpg.278.154.jpg"
]
},
methods: {
},
})
</script>
</body>
</html>