1. Vue.directive 후크 기능(둘 다 선택 사항임)
Vue.directive 에는 bind(바인딩 트리거), insert(삽입 트리거), update(업데이트 트리거), componentUpdated(구성 요소 업데이트 트리거) 및 unbind(바인딩 해제 트리거)의 다섯 가지 내장 후크 기능이 있습니다 .
bind
: 디렉티브가 요소에 처음 바인딩될 때 한 번만 호출됩니다. 여기에서 일회성 초기화 설정을 수행할 수 있습니다. inserted
: 바인딩된 요소가 부모 노드에 삽입될 때 호출됩니다(부모 노드만 존재하는 것이 보장되지만 반드시 문서에 삽입되는 것은 아님). update
: 현재 요소가 제거되지 않는 한 거의 모든 다른 작업은 이 두 가지 수명 주기를 트리거합니다. 먼저 업데이트를 트리거한 다음 componentUpdate를 트리거합니다. 가상 DOM 업데이트 시기: 요소 숨기기, 표시 값 변경, 내용 변경 등이 포함되는 한 가상 DOM 업데이트가 트리거됩니다. :컴포넌트 업데이트: 명령을 사용하는 요소가 언로드될 때 실행됩니다. componentUpdated
현재 요소 입니다 unbind
. 제거되면 한 번만 호출됩니다.
// 注册
Vue.directive( 'my-directive' ,{
bind: function ( ) {
} ,
inserted: function ( ) {
} ,
update: function ( ) {
} ,
componentUpdated: function ( ) {
} ,
unbind: function ( ) {
}
} )
2. 명령 후크 기능은 다음 매개변수로 전달됩니다.
el
: DOM을 직접 조작하는 데 사용할 수 있는 바인딩된 요소를 지정합니다. binding
: 다음 속성을 포함하는 객체: name
: 접두사 v-가 없는 지시문 이름 value
: 지시문의 바인딩 값(예: v-my-directive=") 1+1 ", 바인딩 값은 2입니다 oldValue
. 값이 변경되었는지 여부에 관계없이 업데이트 및 componentUpdated 후크에서만 사용할 수 있는 지시문에 의해 바인딩된 이전 값입니다. expression
: 바운드 값의 문자열 형식입니다. 예를 들어 v-my-directive="1 + 1"인 경우 expression의 값은 "1 + 1"입니다. arg
: 명령에 전달된 매개변수입니다. 예를 들어 v-my-directive:foo에서 arg의 값은 "foo"입니다. modifiers
: 수정자를 포함하는 개체입니다. 예: v-my-directive.foo.bar, 수정자 개체 수정자의 값은 { foo: true, bar: true }입니다. vnode
: Vue 컴파일로 생성된 가상 노드 oldVnode
: 이전 가상 노드, 업데이트 및 componentUpdated 후크에서만 사용 가능 vnode
: vue 컴파일로 생성된 가상 노드 oldVnode
: 이전 가상 노드, 업데이트 및 componentUpdated 후크에서만 사용 가능 除了el之外,其他参数都应该是只读的,不能修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
3. 사용자 지정 명령 시나리오 사용 예
【1】drag.js
import Vue from 'vue'
import {
Message } from 'element-ui' ;
// 自定义指令示例1:弹窗拖拽
Vue.directive( 'dialogDrag' ,{
bind( el,binding,vnode,oldVnode) {
const dialogHeader = el.querySelector( '.el-dialog__header' ) ;
const dialogDom = el.querySelector( '.el-dialog' ) ;
dialogHeader.style.cursor = 'move'
/**
* 不同浏览器获取行内样式属性
* ie浏览器: dom元素.currentStyle
* 火狐浏览器:window.getComputedStyle( dom元素, null)
*/
const sty = dialogDom.currentStyle || window.getComputedStyle( dialogDom, null)
dialogHeader.onmousedown = ( e) = > {
//按下鼠标,获取点击的位置 距离 弹窗的左边和上边的距离
//点击的点距离左边窗口的距离 - 弹窗距离左边窗口的距离
const distX = e.clientX - dialogHeader.offsetLeft;
const distY = e.clientY - dialogHeader.offsetTop;
//弹窗的left属性值和top属性值
let styL, styT
//注意在ie中,第一次获取到的值为组件自带50%,移动之后赋值为Px
if( sty.left.includes( '%' )) {
styL = +document.body.clientWidth * ( +sty.left.replace( /%/g,'' ) / 100 )
styT = +document,body.clientHeight * ( +sty.top.replace( /%/g,'' ) / 100 )
} else{
styL = +sty.left.replace( /px/g,'' ) ;
styT = +sty.top.replace( /px/g,'' ) ;
}
document.onmousemove = function( e) {
//通过事件委托,计算移动距离
const l = e.clientX - distX
const t = e.clientY - distY
//移动当前的元素
dialogDom.style.left = ` ${
l + styL} px`
dialogDom.style.top = ` ${
t + styT} px`
}
document.onmouseup = function( e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
} )
//自定义指令示例2:v-dialogDragWidth 可拖动弹窗宽度(右侧边)
Vue.directive( 'dragWidth' ,{
bind( el) {
const dragDom = el.querySelector( '.el-dialog' ) ;
const lineEl = document.createElement( 'div' ) ;
lineEl.style = 'width: 5px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;' ;
lineEl.addEventListener( 'mousedown' ,
function ( e) {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - el.offsetLeft;
// 当前宽度
const curWidth = dragDom.offsetWidth;
document.onmousemove = function ( e) {
e.preventDefault( ) ; // 移动时禁用默认事件
// 通过事件委托,计算移动的距离
const l = e.clientX - disX;
if( l > 0 ) {
dragDom.style.width = ` ${
curWidth + l} px` ;
}
} ;
document.onmouseup = function ( e) {
document.onmousemove = null;
document.onmouseup = null;
} ;
} , false ) ;
dragDom.appendChild( lineEl) ;
}
} )
// 自定义指令示例3:v-dialogDragWidth 可拖动弹窗高度(右下角)
Vue.directive( 'dragHeight' ,{
bind( el) {
const dragDom = el.querySelector( '.el-dialog' ) ;
const lineEl = document.createElement( 'div' ) ;
lineEl.style = 'width: 6px; background: inherit; height: 10px; position: absolute; right: 0; bottom: 0; margin: auto; z-index: 1; cursor: nwse-resize;' ;
lineEl.addEventListener( 'mousedown' ,
function( e) {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - el.offsetLeft;
const disY = e.clientY - el.offsetTop;
// 当前宽度 高度
const curWidth = dragDom.offsetWidth;
const curHeight = dragDom.offsetHeight;
document.onmousemove = function( e) {
e.preventDefault( ) ; // 移动时禁用默认事件
// 通过事件委托,计算移动的距离
const xl = e.clientX - disX;
const yl = e.clientY - disY
dragDom.style.width = ` ${
curWidth + xl} px` ;
dragDom.style.height = ` ${
curHeight + yl} px` ;
} ;
document.onmouseup = function( e) {
document.onmousemove = null;
document.onmouseup = null;
} ;
} , false ) ;
dragDom.appendChild( lineEl) ;
}
} )
// 自定义指令示例4:图片加载前填充背景色
Vue.directive( 'imgUrl' ,function( el,binding) {
var color = Math.floor( Math.random( ) *1000000) ; //设置随机颜色
el.style.backgroundColor= '#' +color;
var img = new Image( ) ;
img.src= binding.value; // --> binding.value指的是指令后的参数
img.onload= function ( ) {
el.style.backgroundColor= '' ;
el.src= binding.value;
}
} )
// 自定义指令示例5:输入框聚焦
Vue.directive( "focus" , {
// 当被绑定的元素插入到 DOM 中时……
inserted ( el) {
// 聚焦元素
el.querySelector( 'input' ) .focus( )
} ,
} ) ;
// 自定义指令示例6:设置防抖自定义指令
Vue.directive( 'throttle' , {
bind: ( el, binding) = > {
let setTime = binding.value; // 可设置防抖时间
if ( ! setTime) {
// 用户若不设置防抖时间,则默认2s
setTime = 1000 ;
}
let cbFun;
el.addEventListener( 'click' , event = > {
if ( ! cbFun) {
// 第一次执行
cbFun = setTimeout(( ) = > {
cbFun = null;
} , setTime) ;
} else {
/*如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,
它们会按其被添加的顺序被调用。如果在其中一个事件监听器中执行 stopImmediatePropagation( ) ,那么剩下的事件监听器都不会被调用*/
event && event.stopImmediatePropagation( ) ;
}
} , true ) ;
} ,
} ) ;
// 自定义指令示例7:点击按钮操作频繁给出提示
Vue.directive( 'preventReClick' , {
inserted: function ( el, binding) {
el.addEventListener( 'click' , ( ) = > {
if ( ! el.disabled) {
el.disabled = true
Message.warning( '操作频繁' )
setTimeout(( ) = > {
el.disabled = false
//可设置时间
} , binding.value || 3000 )
}
} )
}
} )
[2] main.js에 파일을 소개합니다.
import '@/utils/drag.js'
【3】페이지 사용:
< template>
< div>
< el-button type = "text" @click= "dialogVisible = true" > 点击打开 Dialog< /el-button>
< div style = "display:flex" >
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< img v-imgUrl= "url" > < /img>
< /div>
< div>
< el-input placeholder = "请选择日期" suffix-icon= "el-icon-date" v-model= "input1" > < /el-input>
< el-input v-focus placeholder = "请输入内容" prefix-icon= "el-icon-search" v-model= "input2" > < /el-input>
< /div>
< div>
< el-button type = "danger" v-throttle @click= "throttleBtn" > 危险按钮< /el-button>
< el-button @click= "submitForm()" > 创建< /el-button>
< /div>
< el-dialog title = "提示" v-dialogDrag v-dragWidth v-dragHeight :visible.sync= "dialogVisible" width = "30%" :before-close= "handleClose" >
< span> 这是一段信息< /span>
< span slot = "footer" class = "dialog-footer" >
< el-button @click= "dialogVisible = false" > 取 消< /el-button>
< el-button type = "primary" @click= "dialogVisible = false" > 确 定< /el-button>
< /span>
< /el-dialog>
< /div>
< /template>
< script>
export default {
data ( ) {
return {
dialogVisible: false,
url:'//www.baidu.com/img/flexible/logo/pc/result.png' ,
input1: '' ,
input2: '' ,
}
} ,
methods: {
handleClose( done) {
console.log( '弹窗打开' )
} ,
throttleBtn ( ) {
console.log( '我的用来测试防抖的按钮' )
} ,
submitForm ( ) {
this.$message .error( 'Message 消息提示每次只能1个' ) ;
}
} ,
}
< /script>
< style>
img{
width: 100px;
height: 100px;
}
< /style>
【4】효과:
第二个输入框会鼠标聚焦,
点击按钮,会有防止重复点击
图片加载前有默认背景色
弹窗 可以随处移动。右边可拖拽变宽,右下角可以整体变大