Vue.directive
使用v-*来使用自定义的指令,*代表自定义指令的名称
一、介绍
- Vue中有很多默认内置的指令,如v-model、v-show、v-if等,还可以 通过directive进行自定义指令,对DOM元素进行自定义的操作。
- 在组件内部定义的可以在组件内部使用生效,在APP-mian.js中注册定义的所有组件均可用。
二、钩子函数
一个指令定义对象可以提供如下几个钩子函数(都是可选的)
- bind:只调用一次,可以用于绑定元素的初始化
- insert:被绑定元素插入父节点时调用,此时可以做一些对元素的操作
- unbind:只调用一次,指令与元素解绑时调用
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
三、钩子参数
钩子函数中会被传入的参数
- el:指定绑定的元素,可以直接用这个来对DOM进行操作
- binding:一个对象,包含着指令里面的一些属性
{
name、value、oldValue、expression、arg等
} - vnode:Vue编译生成的虚拟节点
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
四、动态参数
- 说明
实现自定义的组件位置修改(就是参数binding里面传入的arg,接收动态参数,可以根据组件实例来进行动态调节) - 传入动态参数
五、实例
1.需求
-
需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框
-
父组件区域长宽确定
- 如果子组件区域超过父组件会溢出,但是父组件style属性为’overflow: hidden;'发现还是会被框在父组件内部,受制于父组件
当父组件style属性为overflow: hidden;
- 如果子组件区域超过父组件会溢出,但是父组件style属性为’overflow: hidden;'发现还是会被框在父组件内部,受制于父组件
2.如何设置一个类似于传送门一样的指令
-
设置自定义指令v-myportal
-
思路
- 1.把使用指令的组件样式设置为 position:absolute,然后放到app的节点下
- 2.利用f2在页面中的位置确定f3位置,接着把f3的 z-index 样式修改到顶层
-
代码
-
vue组件
<template> <div class="f1class" ref="f1"> 父组件所在div(长宽确定) <div class="f2class" ref="f2"> <div style="height:30px">父组件</div> <button @click="showChildBind" style="height:30px;width:60px"> 子组件 </button> <div style="position:relative;"> <div class="f3class" v-mybind:[bindingargs]="message" v-myportal ref="f3" v-show="childBindshow" > <div> 点击按钮出现的子组件区域 </div> {{message}} </div> </div> </div> <div class="f4class"> 其他父组件 </div> </div> </template> <script> export default { data:function(){ return{ message:'Hello Bind', childBindshow:false, bindingargs:{ top:100, left:20 } } }, methods:{ showChildBind(){//挂载到app以后还需要自己写位置的自适应 this.childBindshow=!this.childBindshow var app = document.querySelector('#app'); var root=this.$refs.f2 var f3comp=this.$refs.f3 f3comp.style.top=(root.getBoundingClientRect().top-app.getBoundingClientRect().top+68)+'px' f3comp.style.left=(root.getBoundingClientRect().left+root.getBoundingClientRect().width/2-30)+'px' } }, directives: { mybindlocal: {//可以在本地注册自定义的指令,用于对DOM元素进行自定义操作 bind: function (el, binding, vnode) { var s = JSON.stringify; let temstr = el.innerHTML; el.innerHTML =temstr + '<br>' + 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'bindarg-top: '+ s(binding.arg.top)+ '<br>'+ 'bindarg-left: '+ s(binding.arg.left)+ '<br>' ; el.style['padding-top']='20px' console.log(el) } } }, } </script> <style> .f1class{ width:600px; height:400px; background:rgb(255,99,71); margin-top:30px; margin-left:30px; position:relative; } .f2class{ width:400px; height:200px; background:rgb(240,230,140); margin-left:30px; margin-top:30px; position:relative; overflow: hidden; } .f3class{ width:500px; height:250px; background:rgb(135,206,235); position:absolute; z-index: 99; } .f4class{ width:200px; height:100px; background:rgb(144,238,144); margin-left:30px; position:relative; } </style>
-
main.js中设置全局的自定义组件
Vue.directive('mybind', {//单纯的自定义指令 bind: function (el, binding, vnode) { var s = JSON.stringify; let temstr = el.innerHTML; el.innerHTML =temstr + '<br>' + 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'bindarg-top: '+ s(binding.arg.top)+ '<br>'+ 'bindarg-left: '+ s(binding.arg.left)+ '<br>' ; el.style['padding-top']='20px' console.log(el) } })
Vue.directive('myportal', {//设置传送门(其实就是简单地挂载到app) inserted: function (el, binding, vnode) {//这是将DOM元素挂载到指定的组件上,注意使用inserted/使用bind刷新会出错 var app = document.querySelector('#app'); var value = binding.value; if(value){ var fathernode=document.querySelector(value) fathernode.appendChild(el) }else{//默认情况下挂载到app下 app.appendChild(el) } } })
-
3.效果
- 点击子组件按钮,子组件附加区域完整的出现且位置正确