vue中自定義指令directive的詳細(xì)指南
一、 什么是自定義指令
我們看到的v-開頭的行內(nèi)屬性,都是指令,不同的指令可以完成或?qū)崿F(xiàn)不同的功能,對普通 DOM元素進(jìn)行底層操作,這時(shí)候就會用到自定義指令。除了核心功能默認(rèn)內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊自定義指令
指令使用的幾種方式:
//會實(shí)例化一個(gè)指令,但這個(gè)指令沒有參數(shù) `v-xxx` // -- 將值傳到指令中 `v-xxx="value"` // -- 將字符串傳入到指令中,如`v-html="'<p>內(nèi)容</p>'"` `v-xxx="'string'"` // -- 傳參數(shù)(`arg`),如`v-bind:class="className"` `v-xxx:arg="value"` // -- 使用修飾符(`modifier`) `v-xxx:arg.modifier="value"`
二、 如何自定義指令
注冊一個(gè)自定義指令有全局注冊與局部注冊
全局注冊注冊主要是用過Vue.directive方法進(jìn)行注冊
Vue.directive第一個(gè)參數(shù)是指令的名字(不需要寫上v-前綴),第二個(gè)參數(shù)可以是對象數(shù)據(jù),也可以是一個(gè)指令函數(shù)
// 注冊一個(gè)全局自定義指令 `v-focus`
Vue.directive('focus', {
// 當(dāng)被綁定的元素插入到 DOM 中時(shí)……
inserted: function (el) {
// 聚焦元素
el.focus() // 頁面加載完成之后自動(dòng)讓輸入框獲取到焦點(diǎn)的小功能
}
})
局部注冊通過在組件options選項(xiàng)中設(shè)置directive屬性
directives: {
focus: {
// 指令的定義
inserted: function (el) {
el.focus() // 頁面加載完成之后自動(dòng)讓輸入框獲取到焦點(diǎn)的小功能
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus property,如下:
<input v-focus />
鉤子函數(shù)
自定義指令也像組件那樣存在鉤子函數(shù):
- bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置
- inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)
- update:所在組件的 VNode 更新時(shí)調(diào)用,但是可能發(fā)生在其子 VNode更新之前。指令的值可能發(fā)生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新
- componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用
- unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用
所有的鉤子函數(shù)的參數(shù)都有以下:
- el:指令所綁定的元素,可以用來直接操作 DOM
- binding:一個(gè)對象,包含以下 property:
`name`:指令名,不包括 v- 前綴。
`value`:指令的綁定值,例如:v-my-directive="1 + 1" 中,綁定值為 2。
`oldValue`:指令綁定的前一個(gè)值,僅在 update 和 componentUpdated 鉤子中可用。無論值是否改變都可用。
`expression`:字符串形式的指令表達(dá)式。例如 v-my-directive="1 + 1" 中,表達(dá)式為 "1 + 1"。
`arg`:傳給指令的參數(shù),可選。例如 v-my-directive:foo 中,參數(shù)為 "foo"。
`modifiers`:一個(gè)包含修飾符的對象。例如:v-my-directive.foo.bar 中,修飾符對象為 { foo: true, bar: true }
`vnode`:Vue 編譯生成的虛擬節(jié)點(diǎn)
`oldVnode`:上一個(gè)虛擬節(jié)點(diǎn),僅在 update 和 componentUpdated 鉤子中可用
除了 el 之外,其它參數(shù)都應(yīng)該是只讀的,切勿進(jìn)行修改。如果需要在鉤子之間共享數(shù)據(jù),建議通過元素的 dataset 來進(jìn)行
舉個(gè)例子:
<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>
三、應(yīng)用場景
使用自定義組件組件可以滿足我們?nèi)粘R恍﹫鼍?,這里給出幾個(gè)自定義組件的案例:
- 防抖
- 圖片懶加載
- 一鍵 Copy的功能
輸入框防抖
防抖這種情況設(shè)置一個(gè)v-throttle自定義指令來實(shí)現(xiàn)
舉個(gè)例子:
// 1.設(shè)置v-throttle自定義指令
Vue.directive('throttle', {
bind: (el, binding) => {
let throttleTime = binding.value; // 防抖時(shí)間
if (!throttleTime) { // 用戶若不設(shè)置防抖時(shí)間,則默認(rèn)2s
throttleTime = 2000;
}
let cbFun;
el.addEventListener('click', event => {
if (!cbFun) { // 第一次執(zhí)行
cbFun = setTimeout(() => {
cbFun = null;
}, throttleTime);
} else {
event && event.stopImmediatePropagation();
}
}, true);
},
});
// 2.為button標(biāo)簽設(shè)置v-throttle自定義指令
<button @click="sayHello" v-throttle>提交</button>
圖片懶加載
設(shè)置一個(gè)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 儲存真實(shí)src
el.setAttribute('src',val);
// 設(shè)置src為loading圖
el.setAttribute('src',def);
},
// 利用InterpObserver監(jiān)聽el
observe(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);
},
// 監(jiān)聽scroll事件
listenerScroll(el){
let handler = LazyLoad.throttle(LazyLoad.load,300);
LazyLoad.load(el);
window.addEventListener('scroll',() => {
handler(el);
});
},
// 加載真實(shí)圖片
load(el){
let windowHeight = document.documentElement.clientHeight
let 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');
}
}
},
// 節(jié)流
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 鉤子函數(shù),第一次綁定時(shí)調(diào)用,可以在這里做初始化設(shè)置
el: 作用的 dom 對象
value: 傳給指令的值,也就是我們要 copy 的值
*/
bind(el, { value }) {
el.$value = value; // 用一個(gè)全局屬性來存?zhèn)鬟M(jìn)來的值,因?yàn)檫@個(gè)值在別的鉤子函數(shù)里還會用到
el.handler = () => {
if (!el.$value) {
// 值為空的時(shí)候,給出提示,我這里的提示是用的 ant-design-vue 的提示,你們隨意
Message.warning('無復(fù)制內(nèi)容');
return;
}
// 動(dòng)態(tài)創(chuàng)建 textarea 標(biāo)簽
const textarea = document.createElement('textarea');
// 將該 textarea 設(shè)為 readonly 防止 iOS 下自動(dòng)喚起鍵盤,同時(shí)將 textarea 移出可視區(qū)域
textarea.readOnly = 'readonly';
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
// 將要 copy 的值賦給 textarea 標(biāo)簽的 value 屬性
textarea.value = el.$value;
// 將 textarea 插入到 body 中
document.body.appendChild(textarea);
// 選中值并復(fù)制
textarea.select();
// textarea.setSelectionRange(0, textarea.value.length);
const result = document.execCommand('Copy');
if (result) {
Message.success('復(fù)制成功');
}
document.body.removeChild(textarea);
};
// 綁定點(diǎn)擊事件,就是所謂的一鍵 copy 啦
el.addEventListener('click', el.handler);
},
// 當(dāng)傳進(jìn)來的值更新的時(shí)候觸發(fā)
componentUpdated(el, { value }) {
el.$value = value;
},
// 指令與元素解綁的時(shí)候,移除事件綁定
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;
};
};
},
},
},
關(guān)于自定義組件還有很多應(yīng)用場景,如:拖拽指令、頁面水印、權(quán)限校驗(yàn)等等應(yīng)用場景
總結(jié)
到此這篇關(guān)于vue中自定義指令directive的文章就介紹到這了,更多相關(guān)vue自定義指令directive內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue頁面中使用getElementsByClassName無法獲取元素的解決
這篇文章主要介紹了vue頁面中使用getElementsByClassName無法獲取元素的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vue+vue-meta-info動(dòng)態(tài)設(shè)置meta標(biāo)簽教程
這篇文章主要介紹了vue+vue-meta-info動(dòng)態(tài)設(shè)置meta標(biāo)簽教程,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue自定義實(shí)例化modal彈窗功能的實(shí)現(xiàn)
這篇文章主要介紹了vue自定義實(shí)例化modal彈窗,Vue.extend 屬于Vue的全局 api,在實(shí)際業(yè)務(wù)開發(fā)中我們很少使用,因?yàn)橄啾瘸S玫?nbsp;Vue.component寫法使用 extend 步驟要更加繁瑣一些,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下2022-09-09
vue2.0 實(shí)現(xiàn)頁面導(dǎo)航提示引導(dǎo)的方法
下面小編就為大家分享一篇vue2.0 實(shí)現(xiàn)頁面導(dǎo)航提示引導(dǎo)的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03
vue基于兩個(gè)計(jì)算屬性實(shí)現(xiàn)選中和全選功能示例
這篇文章主要介紹了vue基于兩個(gè)計(jì)算屬性實(shí)現(xiàn)選中和全選功能,結(jié)合實(shí)例形式分析了vue計(jì)算屬性get及set操作頁面元素實(shí)現(xiàn)選中與全選功能相關(guān)操作技巧,需要的朋友可以參考下2019-02-02
vue3使用axios并封裝axios請求的詳細(xì)步驟
本篇文章分步驟給大家介紹了vue3使用axios并封裝axios請求的詳細(xì)步驟,結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友參考下吧2023-06-06
Vue引入并使用Element組件庫的兩種方式小結(jié)
本文主要介紹了Vue引入并使用Element組件庫的兩種方式小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01

