之前對(duì)代碼中直接調(diào)用DragMove的過程進(jìn)行了封裝匪凡,把它轉(zhuǎn)移到v-dragmove指令中巡李。一切都很美好,數(shù)據(jù)到dom在模板里笆呆,數(shù)據(jù)的處理在組件里请琳。
傳送門: http://www.reibang.com/p/5b20207be9de
之后,寧哥又提了一個(gè)需求赠幕,現(xiàn)在項(xiàng)目中有很多需要記錄位置的功能俄精,這些與業(yè)務(wù)無關(guān)的功能侵蝕了業(yè)務(wù)代碼,使得業(yè)務(wù)代碼臃腫而每次都要做一些重復(fù)的開發(fā)榕堰,這些邏輯能封裝到里面么竖慧?
我說可以啊,不過這樣指令就不只是調(diào)用一下目標(biāo)方法逆屡,參數(shù)坐下適配的適配器的工作了圾旨,多加了一個(gè) 保存位置的代理的功能。這樣這個(gè)指令就相當(dāng)于 適配器 + 代理
了魏蔗。
具體需求是這樣的:路由切換砍的,組件會(huì)銷毀,在切換回來莺治,組件會(huì)重新創(chuàng)建挨约。要求可以在切換回來的時(shí)候恢復(fù)之前的拖動(dòng)位置味混。
分析:恢復(fù)之前的位置需要一個(gè)全局的地方來保存這個(gè)位置,并且需要用唯一的id來關(guān)聯(lián)組件诫惭,位置可以保存在vuex、localStorage蔓挖、自己維護(hù)的一個(gè)內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)等夕土。 這里的數(shù)據(jù)是與業(yè)務(wù)無關(guān)的,而且指令不應(yīng)該依賴vuex瘟判,所以怨绣,我自己維護(hù)了一個(gè)position的Map,而id的話拷获,用元素的id就可以了篮撑,剛好滿足唯一和關(guān)聯(lián)組件兩個(gè)需求。剩下的就是在 dragEndHandler加上保存位置匆瓜、在指令所作用的元素剛插入父元素(inserted方法)時(shí)恢復(fù)位置赢笨。
可視化表達(dá)一下:
代碼如下:
import DragMove from 'drag-move';
const install = (Vue) => {
//所有的拖動(dòng)位置信息
const positionMap = new Map();
//重置位置
const resetPosition = (element, positionId) => {
if (positionMap.has(positionId)) {
const position = positionMap.get(positionId);
element.style += `;left:${position.left}px;top:${position.top}px;`;
}
}
//保存位置
const savePosition = (positionId, position) => {
positionMap.set(positionId, position);
}
//代理拖動(dòng)結(jié)束handler
const proxyDragEndHandler = (dragEndHandler, positionId) => {
return (position) => {
savePosition(positionId, position);
dragEndHandler.apply(null, position);
}
}
/**
* dragmove指令
* 用法: <div id='ele' v-dragmove:ele="{dragend:dragendHandler}"></div>
*/
Vue.directive('dragmove', {
inserted(el, binding) {
const options = {...binding.value, id: binding.arg};
const positionId = options.id;
resetPosition(el, positionId);
if (binding.modifiers.save) {
let dragEndHandler = options.dragend || function() {};
options.dragend = proxyDragEndHandler(dragEndHandler, positionId);
}
new DragMove(options);
}
});
}
module.exports = install;
這里的positionMap就是全局位置數(shù)據(jù)存儲(chǔ)的地方,savePosition和resetPosition就是組件和positionMap之間交流position數(shù)據(jù)的方法驮吱。
經(jīng)過測(cè)試茧妒,是可以滿足需求的,用法如下:
v-dragmove:id.save="{}"
v-dragmove:id="{}"
帶.save修飾符的會(huì)保存位置左冬,否則不會(huì)桐筏。
不過這里有兩個(gè)需要注意的地方,一是因?yàn)閕d直接用的元素id拇砰,需要保證元素id全局唯一梅忌,二是this的問題,如果handler里面用到了this除破,需要手動(dòng)bind一下
v-dragmove:id.save="{dragend:dragEndHanlder.bind(this)}"
封裝的價(jià)值
對(duì)比下重構(gòu)前后的原理圖
沒封裝成指令之前:
封裝成指令之后:
你會(huì)發(fā)現(xiàn)其實(shí)只是把邏輯移動(dòng)了個(gè)位置牧氮,都封裝到指令里,這樣皂岔,會(huì)使得整體可復(fù)用蹋笼。而不是分散的寫多份。封裝的意義就是復(fù)用躁垛。
可以刪掉的一些代碼:
組件中業(yè)務(wù)無關(guān)的邏輯少了很多
收獲
總結(jié)一下開發(fā)這個(gè)指令的收獲:
參數(shù)放到 value和arg里剖毯, 其余的一些控制放到modifer里
如:v-example:arg.a.b.c="value", 這里通過a教馆、b逊谋、c來控制功能,value和arg來傳參指令不應(yīng)該依賴vuex土铺,也不要依賴localStorage胶滋,需要全局?jǐn)?shù)據(jù)的時(shí)候應(yīng)該自己維護(hù)一個(gè)內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)板鬓,比如這里的positionMap。這樣的指令才是通用的究恤。
指令的職責(zé)應(yīng)該單一俭令,比如dragmove這個(gè)指令就只是封裝了拖動(dòng)相關(guān)的邏輯,包括拖動(dòng)位置的保存部宿。
4.vue的指令有一個(gè)坑抄腔,當(dāng)兩個(gè)指令同時(shí)修改了一個(gè)屬性的時(shí)候,沒法保證順序理张,所以dragmove的初始位置是用css設(shè)置的赫蛇,之后通過指令修改style