Vue的自定义指令directive

279 阅读3分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战」。

年后的第三天工作,当我的脑子还没有从要打工的悲伤中走出来,产品的需求就已经追着我跑了
产品:“你看这个搜索能不能让它进入页面就自动对焦呢,提升一下用户的体验感”
我:“应该可以(实际是完全可以,就是不想改)”
但是,答应了就得干啊,打开vscode,开始搞

image.png

Step One

脑子灵光一现,Vue中不是有个自定义指令嘛,快速且方便,说干就干 打开代码,思考了一下是写在全局呢还是写在局部呢 为了后续其他人使用,我当然选择写在全局了,代码如下

<input v-focus class="search_input" v-model="search" type="text" placeholder="搜索分销员名字/手机号" @keyup.13="searchClick" />
// 注册一个全局自定义指令 `v-focus` 
Vue.directive('focus', { 
    // 当被绑定的元素插入到 DOM 中时…… 
    inserted: function (el) { 
    // 聚焦元素 el.focus()
    }
})

上面这段代码也是官网提供的参考代码,当我兴高采烈的打算提交代码的时候,问题也就出现了,输入框并不聚焦

Step Two

我开始找问题出在了哪里,通过打debug发现v-focus进来的el参数并非是input元素,而是父级div元素,找到问题开始解决,这里我将全局定义的自定义指令改为了局部使用 官网给出的代码示例如下:

directives: {
    focus: {
        // 指令的定义
        inserted: function (el) {
            el.focus()
        }
    }
},

参照官网的示例,我将代码修改为以下代码

directives: {
    inserted (el) {
        if (el.tagName.toLocaleLowerCase() == 'input') {
            el.focus()
        } else {
            if (el.getElementsByTagName('input')) {
                el.getElementsByTagName('input')[0].focus()
            }
        }
    }

}

这个时候进入页面聚焦就已经生效了。

Step Three

为了之后能更轻松的插入图片,我有写了一个图片的自定义指令,代码如下

Vue.directive('fail-img', {
	// 当被绑定的元素插入到 DOM 中时……
	inserted: function inserted(el, binding) {
		let url = default_img_url
		switch (binding.value) {
			case 1:
				url = 'https://scdn.yourbay.net/tst/img/tstMinScanImg.jpg'
				break
			case 2:
				url = ''
				break
			case 3:
				url = ''
				break
		}
		setTimeout(function() {
			var img = new Image()
			img.src = el.src
			img.onload = function() {
				el.__loadImg = true
			}
			if (!el.__loadImg) {
				img.onerror = function() {
					setTimeout(function() {
						console.log('error load img:', el.src)
						el.src = url
					}, 300)
				}
			}
		}, 600)
	}
})

这里在说说为什么,这段代码里使用的setTimeout定时器,再写这段代码的时候,我验证的过程中,发现图片src在赋值的时候,组件的DOM并没有生成,挂载不上去,所以采用了定时器的方法延长时间。

Step Four

上述代码采用定时器的方法并不是一个合格的代码,我们可以使用自定义指令的生命周期完善代码

自定义指令的三个参数

在了解生命周期之前,先研究三个参数是什么

  • el: 指令所绑定的元素,可以用来直接操作DOM。
  • binding: 一个对象,包含指令的很多信息。
  • vnode: Vue编译生成的虚拟节点。

自定义指令的生命周期

自定义指令有五个生命周期(也叫钩子函数),分别是 bind,inserted,update,componentUpdated,unbind

  1. bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个绑定时执行一次的初始化动作。
  2. inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
  3. update:被绑定于元素所在的模板更新时调用,而无论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
  4. componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
  5. unbind:只调用一次,指令与元素解绑时调用。