目录

一、 什么是自定义指令

二、 如何自定义指令

三、应用场景实现

输入框防抖

图片懒加载

一键 Copy的功能

拖拽

下拉菜单

相对时间转换


一、 什么是自定义指令

我们看到的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:
  • 除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行

`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 钩子中可用

<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>

三、应用场景

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

输入框防抖

防抖这种情况设置一个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;};};},},}

下拉菜单

点击下拉菜单本身不会隐藏菜单
点击下拉菜单以外的区域隐藏菜单

<script>
Vue.directive('clickoutside', {bind(el, binding) {function documentHandler(e) {if (el.contains(e.target)) {return false }if (binding.expression) {binding.value(e)}}el.__vueMenuHandler__ = documentHandlerdocument.addEventListener('click', el.__vueMenuHandler__)},unbind(el) {document.removeEventListener('click', el.__vueMenuHandler__)delete el.__vueMenuHandler__}
})new Vue({el: '#app',data: {show: false},methods: {handleHide() {this.show = false}}
})
</script>
<div class="main" v-menu="handleHide"><button @click="show = !show">点击显示下拉菜单</button><div class="dropdown" v-show="show"><div class="item"><a href="#">选项 1</a></div><div class="item"><a href="#">选项 2</a></div><div class="item"><a href="#">选项 3</a></div></div>
</div>

相对时间转换

类似微博、朋友圈发布动态后的相对时间,比如刚刚、两分钟前等等

<span v-relativeTime="time"></span>
<script>
new Vue({el: '#app',data: {time: 1565753400000}
})Vue.directive('relativeTime', {bind(el, binding) {// Time.getFormatTime() 方法,自行补充el.innerHTML = Time.getFormatTime(binding.value)el.__timeout__ = setInterval(() => {el.innerHTML = Time.getFormatTime(binding.value)}, 6000)},unbind(el) {clearInterval(el.innerHTML)delete el.__timeout__}
})
</script>

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

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

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

  2. vue中如何自定义指令directive

    一. 什么是自定义指令 我们看到的v-开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能,对普通 DOM元素进行底层操作,这时候就会用到自定义指令.除了核心功能默认内置的指令 (v-mode ...

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

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

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

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

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

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

  6. Vue学习笔记:Vue中封装自定义步骤条 实现上下一步

    Vue中封装自定义步骤条 实现上下一步 效果图: 如上图:在VUE中实现效果,VUE+Element,ant都有封装好的UI,直接引用就好了: 这里,觉得样式不符合UI设计,所以自定义封装了一个步骤条 ...

  7. Vue.js中的自定义指令directives

    除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令.自定义指令,需要声明钩子函数.注册自定义指令也分为注册全局自定义指令和注册局部自定义指令.两者用法相同, ...

  8. 「后端小伙伴来学前端了」关于Vue中的自定义事件,组件绑定自定义事件实现通信

    傍晚的月亮 前言 原本这篇打算写Vue中的那个全局事件总线的原理,但是发现自己少写了这个自定义事件,不讲明白这个自定义事件的操作,不好写全局事件原理,于是就有了这篇文章拉. 一.v-on指令 要讲自定 ...

  9. vue基础4——自定义指令

    自定义指令 自定义指令中的this指向window <div id="root"><h2>当前的n值是:<span v-text="n&qu ...

最新文章

  1. 构建聊天机器人:检索、seq2seq、RL、SeqGAN
  2. sql server management studio 查询的临时文件路径
  3. (0099)iOS开发之Xcode编译工程报错问题汇总
  4. Python Numpy 从文件中读取数据
  5. EF检索中文失败的解决办法
  6. Linux Gedit 打开txt文件乱码
  7. 【PAT乙级】1036 跟奥巴马一起编程 (15 分)
  8. ogg 11.2 for mysql_配置ogg异构mysql-oracle 单向同步
  9. android中的add方法,Android中Fragment怎么addView?
  10. 灰色关联度分析_数学建模|关联分析之术|灰度预测模型预备知识
  11. SAP License:飘在SAP日子之二-售前的产生与发展
  12. 印象笔记添加txt附件
  13. 《人工智能:计算Agent基础》——1.5 复杂性维度
  14. H5 File 对象获取 Input type=file 文件详细信息
  15. 笔记本计算机外壳的制作过程,笔记本电脑外壳选材不可小视
  16. 王道 计算机网络试题讲解_王道考研系列--计算机网络
  17. 【Unity实战100例】Unity制作脑图编辑工具(全网第一首发)
  18. C++三种继承方式--------私有继承(private)
  19. 吴恩达2022机器学习课程评测来了!
  20. 教师评计算机课缺点及建议,评课用语优缺点及建议

热门文章

  1. 2012年系统架构师考试题详解
  2. selenium原理python_Python Selenium的简单演示程序
  3. TCP/IP协议学习之计算机中的端口
  4. 爬虫训练场项目前端基础,Bootstrap5排版、表格、图像
  5. name值为zhangsan
  6. 电子器件系列十九:晶振
  7. MPI(OpenMPI和MPICH(IntelMPI、MVAPICH))和OpenMP
  8. python怎么用sin函数_Python sin() 函数
  9. c 语言 函数 公开课教案6,小班语言公开课教案范文(精选6篇)
  10. ubuntn系统中ssh安装,使用Systemback打镜像出现please remove the installation medium then press enter,丢失安装及拷贝的文件