如何使用vue自定義指令構(gòu)建拖放插件
我們都知道html5的拖放特性,利用它可以很方便的實(shí)現(xiàn)拖拽和放置功能,比如一些選擇類操作的使用場(chǎng)景,讓用戶去拖拽比鼠標(biāo)點(diǎn)擊更容易接受和理解。今天我們就利用這一特性,結(jié)合vue的自定義指令,來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單但是實(shí)用的拖放插件。
為什么叫它插件?因?yàn)槲覀兊哪繕?biāo)不是開發(fā)一個(gè)vue組件,而是兩個(gè)vue的自定義指令,并且最終會(huì)把這兩個(gè)自定義指令封裝到一個(gè)es6的class里,在實(shí)際項(xiàng)目中引入就可以很方便的使用了。
大部分的拖放使用場(chǎng)景都是把一些待選元素從A區(qū)域拖放到B區(qū)域。這里就涉及到兩個(gè)概念,一個(gè)是可拖拽,一個(gè)是可放置,待選元素一定是可以被拖拽的,而目標(biāo)區(qū)域(容器)一定是可以放置的。
如果我們開發(fā)一個(gè)可拖拽的vue組件,或者開發(fā)一個(gè)可放置的組件,那僅僅是這個(gè)組件可拖放,此時(shí)如果需求變更,又需要另外一個(gè)組件也支持拖放,那我們?nèi)孕枰獮榱硪粋€(gè)組件也編寫拖放的代碼。又或者其他項(xiàng)目也需要拖放功能了,我們也要重新開發(fā)。這樣非常不利于維護(hù)和復(fù)用,而vue的自定義指令很好的幫我們解決了這個(gè)問(wèn)題,我們只需要在組件(包括普通的dom元素)上添加自定義指令,就可以使這個(gè)組件(元素)可拖放,這樣就可以靈活的去使用了。
除了核心功能默認(rèn)內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊(cè)自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對(duì)普通 DOM 元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。
綜上,本文的目標(biāo)需要完成兩個(gè)自定義指令:
- v-drag 使組件可拖拽
- v-drop 使組件可放置
目標(biāo)已經(jīng)很明確了,那就開始動(dòng)手吧!由于我們要讓這兩個(gè)指令可在任意組件上發(fā)揮作用,因此需要注冊(cè)Vue全局指令。
Vue.directive('drag', { bind(el, binding, vnode){ //只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。 //在這里可以進(jìn)行一次性的初始化設(shè)置。 } }) Vue.directive('drop', { bind(el, binding, vnode){ // } })
如果你的項(xiàng)目是vue-cli搭建的,你可以把這段代碼寫在main.js里vue初始化的上方。
我們先在drag指令的bind鉤子里編寫代碼,bind只調(diào)用一次,并且是在指令第一次綁定到元素時(shí)調(diào)用,因此我們用了bind鉤子。這個(gè)指令的目標(biāo)是讓組件(元素)可拖拽,所以我們?cè)O(shè)置el的draggable為true
el.draggable = true; el.ondragstart = (event)=>{ event.dataTransfer.setData("Text", "your data..."); }
當(dāng)元素被拖拽時(shí),會(huì)先觸發(fā)ondragstart事件,通常我們都會(huì)在這個(gè)事件里為event的dataTransfer設(shè)置拖拽數(shù)據(jù),目的是當(dāng)元素被放置時(shí),目標(biāo)容器可以獲取拖拽過(guò)來(lái)的數(shù)據(jù),如果拖放不能傳遞數(shù)據(jù),那將是沒(méi)有意義的。上面的代碼調(diào)用dataTransfer的setData方法設(shè)置拖拽數(shù)據(jù),setData的參數(shù)1表示數(shù)據(jù)類型,參數(shù)2表示要傳遞的數(shù)據(jù)。
很不幸,拖拽數(shù)據(jù)目前僅支持字符串,如果你想傳遞復(fù)雜對(duì)象,可以將數(shù)據(jù)序列化
接下來(lái)我們?yōu)閐rop指令的bind鉤子編寫代碼,這個(gè)指令的目的是讓組件(元素)可放置,因此我們需要為元素的ondragover(拖拽經(jīng)過(guò)事件)、ondrop(放置事件)編寫handler,這兩個(gè)handler要阻止事件的默認(rèn)行為。
el.ondragover = (event)=>{ event.preventDefault(); //阻止默認(rèn)行為 } el.ondrop = (event)=>{ event.preventDefault(); let dragData = event.dataTransfer.getData('Text'); //獲取拖拽數(shù)據(jù) }
我們通過(guò)event.dataTransfer的getData方法可以獲取到拖拽開始事件中設(shè)置的拖拽數(shù)據(jù)。
現(xiàn)在你就可以把這兩個(gè)指令加到任何組件上了,加了v-drag的組件可以被拖動(dòng),加了v-drop的組件可以放置并接收拖拽數(shù)據(jù)。
<MyComponent v-drag></MyComponent> <MyContainer v-drop></MyContainer>
新的問(wèn)題來(lái)了,我們進(jìn)行拖拽操作是為了傳遞數(shù)據(jù),然而傳遞數(shù)據(jù)的開始階段,我們是在自定義指令drag的bind鉤子里進(jìn)行的,傳遞數(shù)據(jù)的接收階段,我們是在drop的bind鉤子里進(jìn)行的,那么,數(shù)據(jù)從哪兒來(lái)?到哪兒去?很顯然,數(shù)據(jù)應(yīng)該來(lái)自組件,也應(yīng)該傳遞給另一個(gè)組件,否則我們把指令寫到vue組件上就沒(méi)有任何意義了。
好在自定義指令的鉤子函數(shù)為我們提供了訪問(wèn)組件最簡(jiǎn)單有效的方式:那就是鉤子函數(shù)的第三個(gè)參數(shù)vnode,vnode有一個(gè)屬性是componentInstance,這個(gè)componentInstance就是自定義指令的宿主:vue組件實(shí)例!
接下來(lái)就很容易了,我們只需要為添加了v-drag的組件定義一個(gè)獲取拖拽數(shù)據(jù)的接口,為添加了v-drop的組件定義一個(gè)接收拖拽數(shù)據(jù)的接口即可。雖然vue組件并不支持接口的定義,但我們可以約定好這兩個(gè)方法名,在組件的method中進(jìn)行實(shí)現(xiàn)即可。
//自定義組件內(nèi)部 methods:{ getDragData(){ //約定getDragData為獲取組件拖拽數(shù)據(jù)的接口方法 return this.id; //假設(shè)這個(gè)組件被拖拽時(shí),需要將id傳遞出去 } setDragData(data){ //約定setDragData為組件接收拖拽數(shù)據(jù)的接口方法 this.appendNewChildById(data); //假設(shè)這個(gè)組件接收id來(lái)生成新元素 } }
然后改寫我們自定義指令設(shè)置和傳遞拖拽數(shù)據(jù)的代碼:
let dragValue = ""; if(vnode.componentInstance.getDragData != undefined){ dragValue = vnode.componentInstance.getDragData(); } event.dataTransfer.setData("Text", dragValue);
v-drop指令中的ondrop事件
let dragValue = event.dataTransfer.getData('Text'); if(vnode.componentInstance.setDragData != undefined){ vnode.componentInstance.setDragData(dragValue); }
我們?cè)谠L問(wèn)組件的接口方法時(shí)加了 if 判斷,因?yàn)闆](méi)有接口的約束,組件可能并沒(méi)有實(shí)現(xiàn)這些方法。
好啦,到這里我們已經(jīng)完全實(shí)現(xiàn)了組件拖放的自定義指令,雖然很簡(jiǎn)單,但是很實(shí)用也很靈活,基本可以滿足日常拖拽的需求,讓我們總結(jié)一下整個(gè)流程吧!
- 自定義全局指令 v-drag、v-drop
- 需要拖拽的組件實(shí)現(xiàn)獲取數(shù)據(jù)的接口方法
- 需要放置的組件實(shí)現(xiàn)接收數(shù)據(jù)的接口方法
- drag指令訪問(wèn)組件的接口方法獲取數(shù)據(jù)
- drop指令訪問(wèn)組件的接口方法傳遞數(shù)據(jù)
我們將全局自定義指令的相關(guān)代碼封裝到一個(gè)es6的class里面,并作為一個(gè)單獨(dú)的js文件放到項(xiàng)目里,或者發(fā)布到npm上,然后在main.js里導(dǎo)入這個(gè)類,調(diào)用靜態(tài)初始化方法,即可完成全局指令的注冊(cè)。這樣一來(lái),項(xiàng)目當(dāng)中的任意組件都可以使用v-drag和v-drop了,上面總結(jié)的五個(gè)步驟,只需要實(shí)現(xiàn)第2、3條即可。
以上就是如何使用vue自定義指令構(gòu)建拖放插件的詳細(xì)內(nèi)容,更多關(guān)于vue自定義指令構(gòu)建拖放插件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue AST的轉(zhuǎn)換實(shí)現(xiàn)方法講解
本節(jié),我們將討論關(guān)于AST的轉(zhuǎn)換。所謂AST的轉(zhuǎn)換,指的是對(duì)AST進(jìn)行一系列操作,將其轉(zhuǎn)換為新的AST的過(guò)程。新的AST可以是原語(yǔ)言或原DSL的描述,也可以是其他語(yǔ)言或其他DSL的描述。例如,我們可以對(duì)模板AST進(jìn)行操作,將其轉(zhuǎn)換為JavaScriptAST2023-01-01vue中實(shí)現(xiàn)支持txt,docx,xlsx,mp4格式文件預(yù)覽功能(純前端)
對(duì)于Vue你可以實(shí)現(xiàn)文件的預(yù)覽功能,這篇文章主要給大家介紹了關(guān)于vue中實(shí)現(xiàn)支持txt,docx,xlsx,mp4格式文件預(yù)覽功能的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11基于vue+openlayer實(shí)現(xiàn)地圖聚合和撒點(diǎn)效果
Openlayers 是一個(gè)模塊化、高性能并且功能豐富的WebGIS客戶端的JavaScript包,用于顯示地圖及空間數(shù)據(jù),并與之進(jìn)行交互,具有靈活的擴(kuò)展機(jī)制,本文給大家介紹vue+openlayer實(shí)現(xiàn)地圖聚合效果和撒點(diǎn)效果,感興趣的朋友一起看看吧2021-09-09ElementUI中el-dropdown-item點(diǎn)擊事件無(wú)效問(wèn)題
這篇文章主要介紹了ElementUI中el-dropdown-item點(diǎn)擊事件無(wú)效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04淺談vue引用靜態(tài)資源需要注意的事項(xiàng)
今天小編就為大家分享一篇淺談vue引用靜態(tài)資源需要注意的事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue2 設(shè)置router-view默認(rèn)路徑的實(shí)例
今天小編就為大家分享一篇vue2 設(shè)置router-view默認(rèn)路徑的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09