使用vue自定義指令構(gòu)建拖放插件

我們都知道html5的拖放特性,利用它可以很方便的實現(xiàn)拖拽和放置功能,比如一些選擇類操作的使用場景,讓用戶去拖拽比鼠標點擊更容易接受和理解。今天我們就利用這一特性,結(jié)合vue的自定義指令,來實現(xiàn)一個簡單但是實用的拖放插件。

為什么叫它插件?因為我們的目標不是開發(fā)一個vue組件,而是兩個vue的自定義指令,并且最終會把這兩個自定義指令封裝到一個es6的class里,在實際項目中引入就可以很方便的使用了。

大部分的拖放使用場景都是把一些待選元素從A區(qū)域拖放到B區(qū)域。這里就涉及到兩個概念,一個是可拖拽,一個是可放置,待選元素一定是可以被拖拽的,而目標區(qū)域(容器)一定是可以放置的。

如果我們開發(fā)一個可拖拽的vue組件,或者開發(fā)一個可放置的組件,那僅僅是這個組件可拖放,此時如果需求變更,又需要另外一個組件也支持拖放,那我們?nèi)孕枰獮榱硪粋€組件也編寫拖放的代碼。又或者其他項目也需要拖放功能了,我們也要重新開發(fā)。這樣非常不利于維護和復(fù)用,而vue的自定義指令很好的幫我們解決了這個問題,我們只需要在組件(包括普通的dom元素)上添加自定義指令,就可以使這個組件(元素)可拖放,這樣就可以靈活的去使用了。

除了核心功能默認內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自定義指令。

綜上,本文的目標需要完成兩個自定義指令:

  • v-drag 使組件可拖拽
  • v-drop 使組件可放置

目標已經(jīng)很明確了,那就開始動手吧!由于我們要讓這兩個指令可在任意組件上發(fā)揮作用,因此需要注冊Vue全局指令。

Vue.directive('drag', {
   bind(el, binding, vnode){
        //只調(diào)用一次,指令第一次綁定到元素時調(diào)用。
        //在這里可以進行一次性的初始化設(shè)置。
    }
})
Vue.directive('drop', {
   bind(el, binding, vnode){
        //
    }
})

如果你的項目是vue-cli搭建的,你可以把這段代碼寫在main.js里vue初始化的上方。

我們先在drag指令的bind鉤子里編寫代碼,bind只調(diào)用一次,并且是在指令第一次綁定到元素時調(diào)用,因此我們用了bind鉤子。這個指令的目標是讓組件(元素)可拖拽,所以我們設(shè)置el的draggable為true

el.draggable = true;
el.ondragstart = (event)=>{
  event.dataTransfer.setData("Text", "your data...");
}

當元素被拖拽時,會先觸發(fā)ondragstart事件,通常我們都會在這個事件里為event的dataTransfer設(shè)置拖拽數(shù)據(jù),目的是當元素被放置時,目標容器可以獲取拖拽過來的數(shù)據(jù),如果拖放不能傳遞數(shù)據(jù),那將是沒有意義的。上面的代碼調(diào)用dataTransfer的setData方法設(shè)置拖拽數(shù)據(jù),setData的參數(shù)1表示數(shù)據(jù)類型,參數(shù)2表示要傳遞的數(shù)據(jù)。

很不幸,拖拽數(shù)據(jù)目前僅支持字符串,如果你想傳遞復(fù)雜對象,可以將數(shù)據(jù)序列化

接下來我們?yōu)閐rop指令的bind鉤子編寫代碼,這個指令的目的是讓組件(元素)可放置,因此我們需要為元素的ondragover(拖拽經(jīng)過事件)、ondrop(放置事件)編寫handler,這兩個handler要阻止事件的默認行為。

el.ondragover = (event)=>{
  event.preventDefault(); //阻止默認行為
}
el.ondrop = (event)=>{
  event.preventDefault();
  let dragData = event.dataTransfer.getData('Text'); //獲取拖拽數(shù)據(jù)
}

我們通過event.dataTransfer的getData方法可以獲取到拖拽開始事件中設(shè)置的拖拽數(shù)據(jù)。

現(xiàn)在你就可以把這兩個指令加到任何組件上了,加了v-drag的組件可以被拖動,加了v-drop的組件可以放置并接收拖拽數(shù)據(jù)。

<MyComponent v-drag></MyComponent>

<MyContainer v-drop></MyContainer>

新的問題來了,我們進行拖拽操作是為了傳遞數(shù)據(jù),然而傳遞數(shù)據(jù)的開始階段,我們是在自定義指令drag的bind鉤子里進行的,傳遞數(shù)據(jù)的接收階段,我們是在drop的bind鉤子里進行的,那么,數(shù)據(jù)從哪兒來?到哪兒去?很顯然,數(shù)據(jù)應(yīng)該來自組件,也應(yīng)該傳遞給另一個組件,否則我們把指令寫到vue組件上就沒有任何意義了。

好在自定義指令的鉤子函數(shù)為我們提供了訪問組件最簡單有效的方式:那就是鉤子函數(shù)的第三個參數(shù)vnode,vnode有一個屬性是componentInstance,這個componentInstance就是自定義指令的宿主:vue組件實例!

接下來就很容易了,我們只需要為添加了v-drag的組件定義一個獲取拖拽數(shù)據(jù)的接口,為添加了v-drop的組件定義一個接收拖拽數(shù)據(jù)的接口即可。雖然vue組件并不支持接口的定義,但我們可以約定好這兩個方法名,在組件的method中進行實現(xiàn)即可。

//自定義組件內(nèi)部
methods:{
  getDragData(){ //約定getDragData為獲取組件拖拽數(shù)據(jù)的接口方法
    return this.id; //假設(shè)這個組件被拖拽時,需要將id傳遞出去
  }
  setDragData(data){ //約定setDragData為組件接收拖拽數(shù)據(jù)的接口方法
    this.appendNewChildById(data); //假設(shè)這個組件接收id來生成新元素
  }
}

然后改寫我們自定義指令設(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);
}

我們在訪問組件的接口方法時加了 if 判斷,因為沒有接口的約束,組件可能并沒有實現(xiàn)這些方法。

好啦,到這里我們已經(jīng)完全實現(xiàn)了組件拖放的自定義指令,雖然很簡單,但是很實用也很靈活,基本可以滿足日常拖拽的需求,讓我們總結(jié)一下整個流程吧!

  • 自定義全局指令 v-drag、v-drop
  • 需要拖拽的組件實現(xiàn)獲取數(shù)據(jù)的接口方法
  • 需要放置的組件實現(xiàn)接收數(shù)據(jù)的接口方法
  • drag指令訪問組件的接口方法獲取數(shù)據(jù)
  • drop指令訪問組件的接口方法傳遞數(shù)據(jù)

我們將全局自定義指令的相關(guān)代碼封裝到一個es6的class里面,并作為一個單獨的js文件放到項目里,或者發(fā)布到npm上,然后在main.js里導入這個類,調(diào)用靜態(tài)初始化方法,即可完成全局指令的注冊。這樣一來,項目當中的任意組件都可以使用v-drag和v-drop了,上面總結(jié)的五個步驟,只需要實現(xiàn)第2、3條即可。

這個案例的全部代碼我會抽時間發(fā)布到npm上,也會同步更新到我的github賬戶,如果大家急需,請在評論區(qū)留言進行索取,也歡迎大家針對這個案例發(fā)表不同意見。

歡迎大家在評論區(qū)留言自己想了解的前端話題,我會繼續(xù)推出更多精彩的文章!

原創(chuàng)不易,有錢的捧個錢場,給個打賞

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 基于Vue的一些資料 內(nèi)容 UI組件 開發(fā)框架 實用庫 服務(wù)端 輔助工具 應(yīng)用實例 Demo示例 element★...
    嘗了又嘗閱讀 1,275評論 0 1
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    小姜先森o0O閱讀 10,094評論 0 72
  • UI組件 element - 餓了么出品的Vue2的web UI工具套件 Vux - 基于Vue和WeUI的組件庫...
    流過閱讀 3,455評論 1 35
  • UI組件 element- 餓了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的組件庫 m...
    你猜_3214閱讀 11,332評論 0 118
  • 11個超棒的 iOS 開發(fā)學習網(wǎng)站 iOS應(yīng)用性能調(diào)優(yōu)的25個建議和技巧 如何讓iOS 保持界面流暢?這些技巧你知...
    Ice丶澤閱讀 268評論 0 0

友情鏈接更多精彩內(nèi)容