Vue自定義指令directive的使用方法分享
1. 一個(gè)指令定義對象可以提供如下幾個(gè)鉤子函數(shù)(均為可選)
bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。
inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用(僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
update:只要當(dāng)前元素不被移除,其他操作幾乎都會(huì)觸發(fā)這2個(gè)生命周期,先觸發(fā)update后觸發(fā)componentUpdate。虛擬DOM什么時(shí)候更新:只要涉及到元素的隱藏、顯示(display)值的改變、內(nèi)容的改變等都會(huì)觸發(fā)虛擬DOM更新.
componentUpdated:組件更新
unbind:當(dāng)使用指令的元素被卸載的時(shí)候會(huì)執(zhí)行,就是當(dāng)前元素被移除的時(shí)候,只調(diào)用一次
Vue.directive 內(nèi)置了五個(gè)鉤子函數(shù) :
bind(綁定觸發(fā))、inserted(插入觸發(fā))、update(更新觸發(fā))、componentUpdated(組件更新觸發(fā))和unbind(解綁觸發(fā))函
// 注冊
???
Vue.directive(
'my-directive'
,{
???????
bind:?
function
() {},????????
???????
inserted:
function
() {},
???????
update:
function
() {},
???????
componentUpdated:
function
() {},
???????
unbind:
function
() {}
???
})
2.指令鉤子函數(shù)會(huì)被傳入以下參數(shù)
el:指定所綁定的元素,可以用來直接操作DOM
binding:一個(gè)對象,包含以下屬性:
name:指令名,不包含前綴v-
value:指令的綁定值,例如:v-my-directive="1+1"中,綁定值為2
oldValue:指令綁定的前一個(gè)值,僅在update和componentUpdated鉤子中可用,無論值是否改變都可用。
expression: 綁定值的字符串形式。 例如 v-my-directive=“1 + 1” , expression 的值是 “1 + 1”。
arg: 傳給指令的參數(shù)。例如 v-my-directive:foo, arg 的值是 “foo”。
modifiers: 一個(gè)包含修飾符的對象。 例如: v-my-directive.foo.bar, 修飾符對象 modifiers 的值是{ foo: true, bar: true }。
vnode: Vue 編譯生成的虛擬節(jié)點(diǎn)
oldVnode: 上一個(gè)虛擬節(jié)點(diǎn),僅在 update 和 componentUpdated 鉤子中可用
vnode:vue編譯生成的虛擬節(jié)點(diǎn)
oldVnode:上一個(gè)虛擬節(jié)點(diǎn),僅在update和componentUpdated鉤子中可用
除了el之外,其他參數(shù)都應(yīng)該是只讀的,不能修改。如果需要在鉤子之間共享數(shù)據(jù),建議通過元素的 dataset 來進(jìn)行。
3.使用自定義指令場景的示例
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'
????????
/**
?????????
* 不同瀏覽器獲取行內(nèi)樣式屬性
?????????
* ie瀏覽器:?????? dom元素.currentStyle
?????????
* 火狐瀏覽器:window.getComputedStyle(dom元素, null)
????????
*/
????????
const sty = dialogDom.currentStyle || window.getComputedStyle(dialogDom,
null
)
????????
dialogHeader.onmousedown = (e) => {
????????????
//按下鼠標(biāo),獲取點(diǎn)擊的位置 距離 彈窗的左邊和上邊的距離
????????????
//點(diǎn)擊的點(diǎn)距離左邊窗口的距離 - 彈窗距離左邊窗口的距離
????????????
const distX = e.clientX - dialogHeader.offsetLeft;
????????????
const distY = e.clientY - dialogHeader.offsetTop;
????????????
//彈窗的left屬性值和top屬性值
????????????
let styL, styT
????????????
//注意在ie中,第一次獲取到的值為組件自帶50%,移動(dòng)之后賦值為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) {
????????????????
//通過事件委托,計(jì)算移動(dòng)距離
????????????????
const l = e.clientX - distX
????????????????
const t = e.clientY - distY
????????????????
//移動(dòng)當(dāng)前的元素
????????????????
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 可拖動(dòng)彈窗寬度(右側(cè)邊)
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) {
??????????????
// 鼠標(biāo)按下,計(jì)算當(dāng)前元素距離可視區(qū)的距離
??????????????
const disX = e.clientX - el.offsetLeft;
??????????????
// 當(dāng)前寬度
??????????????
const curWidth = dragDom.offsetWidth;
??????????????
document.onmousemove =
function
(e) {
??????????????????
e.preventDefault();
// 移動(dòng)時(shí)禁用默認(rèn)事件
??????????????????
// 通過事件委托,計(jì)算移動(dòng)的距離
??????????????????
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 可拖動(dòng)彈窗高度(右下角)
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) {
??????????????
// 鼠標(biāo)按下,計(jì)算當(dāng)前元素距離可視區(qū)的距離
??????????????
const disX = e.clientX - el.offsetLeft;
??????????????
const disY = e.clientY - el.offsetTop;
??????????????
// 當(dāng)前寬度 高度
??????????????
const curWidth = dragDom.offsetWidth;
??????????????
const curHeight = dragDom.offsetHeight;
??????????????
document.onmousemove =
function
(e) {
??????????????????
e.preventDefault();
// 移動(dòng)時(shí)禁用默認(rèn)事件
??????????????????
// 通過事件委托,計(jì)算移動(dòng)的距離
??????????????????
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);
//設(shè)置隨機(jī)顏色
????
el.style.backgroundColor=
'#'
+color;
???
?????
var
img=
new
Image();
????
img.src=binding.value;
// -->binding.value指的是指令后的參數(shù)
????
img.onload=
function
(){
??????
el.style.backgroundColor=
''
;
??????
el.src=binding.value;?????
????
}
??
})
// 自定義指令示例5:輸入框聚焦
Vue.directive(
"focus"
, {
????
// 當(dāng)被綁定的元素插入到 DOM 中時(shí)……
????
inserted (el) {
????????
// 聚焦元素
????????
el.querySelector(
'input'
).focus()
????
},
??
});
// 自定義指令示例6:設(shè)置防抖自定義指令
Vue.directive(
'throttle'
, {
????
bind: (el, binding) => {
??????
let setTime = binding.value;
// 可設(shè)置防抖時(shí)間
??????
if
(!setTime) {
// 用戶若不設(shè)置防抖時(shí)間,則默認(rèn)2s
????????
setTime = 1000;
??????
}
??????
let cbFun;
??????
el.addEventListener(
'click'
, event => {
????????
if
(!cbFun) {
// 第一次執(zhí)行
??????????
cbFun = setTimeout(() => {
????????????
cbFun =
null
;
??????????
}, setTime);
????????
}
else
{
????????????
/*如果多個(gè)事件監(jiān)聽器被附加到相同元素的相同事件類型上,當(dāng)此事件觸發(fā)時(shí),
????????????
它們會(huì)按其被添加的順序被調(diào)用。如果在其中一個(gè)事件監(jiān)聽器中執(zhí)行 stopImmediatePropagation() ,那么剩下的事件監(jiān)聽器都不會(huì)被調(diào)用*/
??????????
event && event.stopImmediatePropagation();
????????
}
??????
},
true
);
????
},
??
});
// 自定義指令示例7:點(diǎn)擊按鈕操作頻繁給出提示
??
Vue.directive(
'preventReClick'
, {
????
inserted:
function
(el, binding) {
??????
el.addEventListener(
'click'
, () => {
????????
if
(!el.disabled) {
??????????
el.disabled =
true
??????????
Message.warning(
'操作頻繁'
)
??????????
setTimeout(() => {
????????????
el.disabled =
false
????????????
//可設(shè)置時(shí)間
??????????
}, binding.value || 3000)
????????
}
??????
})
????
}
})
main.js中引入文件:
import
'@/assets/js/drag.js'
頁面使用:
<
template
>
??
<
div
>
????
<
el-button
type
=
"text"
@
click
=
"dialogVisible = true"
>點(diǎn)擊打開 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
=
"請輸入內(nèi)容"
prefix-icon
=
"el-icon-search"
v-model
=
"input2"
></
el-input
>
????
</
div
>
????
<
div
>
??????
<
el-button
type
=
"danger"
v-throttle @
click
=
"throttleBtn"
>危險(xiǎn)按鈕</
el-button
>
??????
<
el-button
@
click
=
"submitForm()"
>創(chuàng)建</
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個(gè)');
????
}
??
},
}
</
script
>
<
style
>
img{
??
width: 100px;
??
height: 100px;
}
</
style
>
看下效果吧:
首先進(jìn)入頁面,
1.第二個(gè)輸入框會(huì)鼠標(biāo)聚焦,
2.點(diǎn)擊按鈕,會(huì)有防止重復(fù)點(diǎn)擊
3.圖片加載前有默認(rèn)背景色
4.彈窗 可以隨處移動(dòng)。右邊可拖拽變寬,右下角可以整體變大
