一、 什么是自定义指令

我们看到的v-开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能,对普通 DOM元素进行底层操作,这时候就会用到自定义指令。除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令

指令使用的几种方式:

//会实例化一个指令,但这个指令没有参数
`v-xxx`// -- 将值传到指令中
`v-xxx="value"`  // -- 将字符串传入到指令中,如`v-html="'<p>内容</p>'"`
`v-xxx="'string'"` // -- 传参数(`arg`),如`v-bind:class="className"`
`v-xxx:arg="value"` // -- 使用修饰符(`modifier`)
`v-xxx:arg.modifier="value"` 

二、 如何自定义指令

注册一个自定义指令有全局注册与局部注册

全局注册注册主要是用过Vue.directive方法进行注册

Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {// 当被绑定的元素插入到 DOM 中时……inserted: function (el) {// 聚焦元素el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能}
})

局部注册通过在组件options选项中设置directive属性

directives: {focus: {// 指令的定义inserted: function (el) {el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能}}
}

然后你可以在模板中任何元素上使用新的 v-focus property,如下:

<input v-focus />

钩子函数

自定义指令也像组件那样存在钩子函数:

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用

  • unbind:只调用一次,指令与元素解绑时调用

所有的钩子函数的参数都有以下:

  • el:指令所绑定的元素,可以用来直接操作 DOM

  • binding:一个对象,包含以下 property:

    
    `name`:指令名,不包括 v- 前缀。`value`:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。`oldValue`:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。`expression`:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。`arg`:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。`modifiers`:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }`vnode`:Vue 编译生成的虚拟节点`oldVnode`:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用
    

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行
举个例子:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
<script>Vue.directive('demo', function (el, binding) {console.log(binding.value.color) // "white"console.log(binding.value.text)  // "hello!"})
</script>

三、应用场景

使用自定义组件组件可以满足我们日常一些场景,这里给出几个自定义组件的案例:

  • 防抖

  • 图片懒加载

  • 一键 Copy的功能

输入框防抖

防抖这种情况设置一个v-throttle自定义指令来实现

举个例子:


// 1.设置v-throttle自定义指令
Vue.directive('throttle', {bind: (el, binding) => {let throttleTime = binding.value; // 防抖时间if (!throttleTime) { // 用户若不设置防抖时间,则默认2sthrottleTime = 2000;}let cbFun;el.addEventListener('click', event => {if (!cbFun) { // 第一次执行cbFun = setTimeout(() => {cbFun = null;}, throttleTime);} else {event && event.stopImmediatePropagation();}}, true);},
});
// 2.为button标签设置v-throttle自定义指令
<button @click="sayHello" v-throttle>提交</button>

图片懒加载

设置一个v-lazy自定义组件完成图片懒加载

const LazyLoad = {// install方法install(Vue,options){// 代替图片的loading图let defaultSrc = options.default;Vue.directive('lazy',{bind(el,binding){LazyLoad.init(el,binding.value,defaultSrc);},inserted(el){// 兼容处理if('InterpObserver' in window){LazyLoad.observe(el);}else{LazyLoad.listenerScroll(el);}},})},// 初始化init(el,val,def){// src 储存真实srcel.setAttribute('src',val);// 设置src为loading图el.setAttribute('src',def);},// 利用InterpObserver监听elobserve(el){let io = new InterpObserver(entries => {let realSrc = el.dataset.src;if(entries[0].isIntersecting){if(realSrc){el.src = realSrc;el.removeAttribute('src');}}});io.observe(el);},// 监听scroll事件listenerScroll(el){let handler = LazyLoad.throttle(LazyLoad.load,300);LazyLoad.load(el);window.addEventListener('scroll',() => {handler(el);});},// 加载真实图片load(el){let windowHeight = document.documentElement.clientHeightlet elTop = el.getBoundingClientRect().top;let elBtm = el.getBoundingClientRect().bottom;let realSrc = el.dataset.src;if(elTop - windowHeight<0&&elBtm > 0){if(realSrc){el.src = realSrc;el.removeAttribute('src');}}},// 节流throttle(fn,delay){let timer; let prevTime;return function(...args){let currTime = Date.now();let context = this;if(!prevTime) prevTime = currTime;clearTimeout(timer);if(currTime - prevTime > delay){prevTime = currTime;fn.apply(context,args);clearTimeout(timer);return;}timer = setTimeout(function(){prevTime = Date.now();timer = null;fn.apply(context,args);},delay);}}}
export default LazyLoad;

一键 Copy的功能

import { Message } from 'ant-design-vue';const vCopy = { ///*bind 钩子函数,第一次绑定时调用,可以在这里做初始化设置el: 作用的 dom 对象value: 传给指令的值,也就是我们要 copy 的值*/bind(el, { value }) {el.$value = value; // 用一个全局属性来存传进来的值,因为这个值在别的钩子函数里还会用到el.handler = () => {if (!el.$value) {// 值为空的时候,给出提示,我这里的提示是用的 ant-design-vue 的提示,你们随意Message.warning('无复制内容');return;}// 动态创建 textarea 标签const textarea = document.createElement('textarea');// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域textarea.readOnly = 'readonly';textarea.style.position = 'absolute';textarea.style.left = '-9999px';// 将要 copy 的值赋给 textarea 标签的 value 属性textarea.value = el.$value;// 将 textarea 插入到 body 中document.body.appendChild(textarea);// 选中值并复制textarea.select();// textarea.setSelectionRange(0, textarea.value.length);const result = document.execCommand('Copy');if (result) {Message.success('复制成功');}document.body.removeChild(textarea);};// 绑定点击事件,就是所谓的一键 copy 啦el.addEventListener('click', el.handler);},// 当传进来的值更新的时候触发componentUpdated(el, { value }) {el.$value = value;},// 指令与元素解绑的时候,移除事件绑定unbind(el) {el.removeEventListener('click', el.handler);},
};export default vCopy;

拖拽

<div ref="a" id="bg" v-drag></div>directives: {drag: {bind() {},inserted(el) {el.onmousedown = (e) => {let x = e.clientX - el.offsetLeft;let y = e.clientY - el.offsetTop;document.onmousemove = (e) => {let xx = e.clientX - x + "px";let yy = e.clientY - y + "px";el.style.left = xx;el.style.top = yy;};el.onmouseup = (e) => {document.onmousemove = null;};};},},},

关于自定义组件还有很多应用场景,如:拖拽指令、页面水印、权限校验等等应用场景

vue中如何自定义指令directive相关推荐

  1. vue中通过自定义指令将汉字转化为首字母大写、首字母小写、大写、小写的拼音

    使用情景: 在文本框中输入内容,例如姓名 在页面中将姓名转化为姓名的拼音,包括大写拼音.小写拼音.首字母大写拼音.大写拼音缩写.小写拼音缩写 新建一个 pinyin.js 文件 这是一串又臭又长的un ...

  2. vue中如何自定义指令

    目录 一. 什么是自定义指令 二. 如何自定义指令 三.应用场景实现 输入框防抖 图片懒加载 一键 Copy的功能 拖拽 下拉菜单 相对时间转换 一. 什么是自定义指令 我们看到的v-开头的行内属性, ...

  3. 手把手教你在vue中使用自定义指令全局封装防抖节流函数

    第一步在src下创建utils文件夹并创建common.js文件 //'@/utils/common.js' 文件function throttle(bindObj, fn, delay) {bind ...

  4. vue从入门到进阶:自定义指令directive,插件的封装以及混合mixins(七)

    一.自定义指令directive 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令.注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件.然而,有的 ...

  5. Vue项目中的自定义指令

    Vue项目中的自定义指令 使用场景:需要对DOM元素进行底层操作 局部自定义指令 当指令第一次被绑定到元素上的时候,会立即触发 bind() 1 定义 与 data 同级 在 directives 节 ...

  6. 29.VUE自定义指令directive和inserted

    VUE自定义指令directive和inserted 1.什么事自定义指令 1.2使用自定义指令的方式 2.设置自定义组件 2.1 设置全局指令 2.2 设置私有指令 2.3 钩子函数 3. 案例 1 ...

  7. 第三十九篇 自定义指令 - directive

    前面讲了关于在Vue中如何来进行封装swiper组件的内容,本篇内容讲到使自定义组件,讲这块内容也是同样为了后续再次回顾封装swiper组件变化做铺垫内容,那么什么是自定义指令,在前面的内容讲过了好些 ...

  8. Vue3 学习笔记 —— 破坏式更新、自定义指令 directive

    目录 1. 什么叫破坏式更新? 2. Vue3 中的自定义指令 2.1 自定义指令的生命周期 2.1.1 Vue2 Vs Vue3 的自定义指令生命周期 2.1.2 自定义指令的生命周期中,接收的参数 ...

  9. vue单文件自定义指令的封装

        vue单文件自定义指令的封装 第一步(封装一个js文件) // 暴露并配置指令 功能添加背景颜色 export const mycolor = {// 解析结构完成后自动运行钩子函数inser ...

最新文章

  1. HDU 1816, POJ 2723 Get Luffy Out(2-sat)
  2. 最近看的篇文章,年过三十,感触很深
  3. solr 使用自定义的 QueryParser
  4. Redis开发:hash存储自定义Java对象及value的序列化器设置
  5. /MD, /MDD, /ML, /MT,/MTD(使用运行时库)
  6. Failed to instantiate [org.springframework.test.context.web.ServletTestExecute
  7. hihoCoder 1257 Snake Carpet(很简单的构造方法)
  8. python安全攻防---爬虫基础---get和post提交数据
  9. 计算机知识竞赛决赛流程,计算机知识竞赛决赛圆满结束!还不快戳?!
  10. 常量池在堆还是方法区_详解Java 虚拟机(第③篇)——String 类和常量池
  11. APP UI设计作品素材,还没灵感就撞墙吧!
  12. android怎么用经纬度定位,android 依据经纬度定位所在城市
  13. layui 导航栏设置无鼠标停留特效_五款最受欢迎的热门wordpress开源主题 - 博客、导航...
  14. 吉比特2018校招 走格子游戏
  15. Linux 流量监控
  16. 淘特,阿里在下沉市场的一把好刀
  17. agv机器人无人仓系统-开源agv控制系统opentcs
  18. thinking php 教程,Thinkphp基础教程
  19. Ymir平台部署安装
  20. NTT DATA处女面

热门文章

  1. 在HTML中设置锚点常用的有三种方式
  2. 杭电OJ1214圆桌会议换座位
  3. 爬虫实例(naruto)
  4. 开发环境、测试环境、生产环境、UAT环境、仿真环境详解
  5. 贵州省大数据发展驶入“信息高速公路”
  6. matplotlib部分中文字体的英文名
  7. 高精度随流检测技术助力金融行业实现智能运维
  8. HTTPS和HTTP有什么不一样?如何申请SSL(HTTPS)证书?
  9. 中国燃气发电机组行业运行前景分析与投资战略规划报告2022-2028年
  10. Linux各类常用操作