本文轉載於SegmentFault社區
作者:Ray_
在使用 Element 組件庫時,有些需求功能是組件庫不帶有的,使用了自定義指令來解決這些問題
代碼倉庫vue-element-utils
也提供了 npm 包下載使用
入口文件引入:
例如:
Select 滾動加載(懶加載)
Dialog 拖拽位置
Dialog 拖拽寬高
Vue自定義指令概念
鉤子函數Vue 提供了內置指令v-model,v-show等,也允許註冊自定義指令。多數情況中代碼的復用和抽象的主要形式是組件。有的情況你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自定義指令。
bind: 只調用一次,指令第一次綁定到元素時調用。這裡可以進行一次性的初始化設置
inserted: 被綁定元素插入父節點時調用(僅保證父節點存在,但不一定已被插入文檔中)
update: 所在組件的 VNode 更新時調用
componentUpdated: 所在組件的 VNode 及其子 VNode 全部更新後調用
unbind: 只調用一次,指令與元素解綁時調用
除了el之外,其它參數都應該是只讀的。如果需要在鉤子之間共享數據,建議通過元素的dataset來進行。
el: 指令所綁定的元素,可以用來直接操作 DOM
binding: 一個對象,包含以下 property:
name: 指令名,不包括v-前綴
value: 指令的綁定值,例如:v-my-directive="1 + 1"中,綁定值為2
oldValue: 指令綁定的前一個值,僅在update和componentUpdated鉤子中可用。無論值是否改變都可用
expression: 字符串形式的指令表達式。例如:v-my-directive="1 + 1"中,表達式為"1 + 1"
arg: 傳給指令的參數,可選。例如v-my-directive:foo中, 修飾符對象為"foo"
modifiers: 一個包含修飾符的對象. 例如v-my-directive.foo.bar中, 修飾符對象為{ foo: true, bar: true }
vnode: Vue 編譯生成的虛擬節點
oldVnode: 上一個虛擬節點, 僅在update和componentUpdated鉤子中可用
Select 滾動加載(懶加載)
當 Select 組件需要展示的數據非常多的時候,滾動加載可以作為一種優化手段,當然還有其它更好的方式,可以參考Vue項目優化總結。
展示:
selectWrap.addEventListener( 'scroll', function( ) { /*** scrollHeight 獲取元素內容高度(只讀)** scrollTop 獲取或者設置元素的偏移量,常用於:計算滾動條的位置,當一個元素的容器沒有產生方向的滾動條,那它的 scrollTop 的值默認為0** clientHeight 讀取元素的可見高度(只讀)** 如果元素滾動到底, 等式`ele.scrollHeight - ele.scrollTop === ele.clientHeight;`返回 true, 沒有則返回 false*/constcondition = this.scrollHeight - this.scrollTop <= this.clientHeight; condition && binding.value;});}}
Dialog 拖動
展示:
// 獲取原有屬性 ie dom 元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);conststy = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
dialogHeaderEl.onmousedown = ( e) => { // 滑鼠按下,計算當前元素距離可視區的距離constdisX = e.clientX - dialogHeaderEl.offsetLeft; constdisY = e.clientY - dialogHeaderEl.offsetTop;
// 獲取到的值帶 px 正則匹配替換letstyL, styT;
// 注意在 ie 中 第一次獲取到的值為組件自帶 50% 移動之後賦值為 pxif(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) { // 通過事件委託,計算移動的距離constl = e.clientX - disX; constt = e.clientY - disY;
// 移動當前元素dragDom.style.left = ` ${l + styL}px` ; dragDom.style.top = ` ${t + styT}px` ;
//將此時的位置傳出去//binding.value({x:e.pageX,y:e.pageY})};
document.onmouseup = function( e) { document.onmousemove = null; document.onmouseup = null; };};}}
Dialog 拖拽寬度
展示:
// 當前寬度constcurWidth = dragDom.offsetWidth;
document.onmousemove = function( e) { e.preventDefault; // 移動時禁用默認事件
// 通過事件委託,計算移動的距離constl = e.clientX - disX;
dragDom.style.width = ` ${curWidth + l}px` ; };
document.onmouseup = function( e) { document.onmousemove = null; document.onmouseup = null; };},false);dragDom.(lineEl);}}
clipboard 剪切板
展示:
functionclipboard( text) { returnnewPromise( ( reslove, reject) => { constinput = document.( 'input'); input.setAttribute( 'readonly', 'readonly'); input.setAttribute( 'value', text); document.body.(input); input.focus;input.setSelectionRange( 0, 9999); if( document.execCommand( 'copy')) { document.execCommand( 'copy'); reslove(text);} else{ reject( newError( '複製失敗')); }document.body.removeChild(input); });}const$clipboard = clipboard;
export{ $clipboard}
exportdefault{ bind: function( el, binding, vnode) { if(binding.arg === 'success') { el._clipboard_success = binding.value;} elseif(binding.arg === 'error') { el._clipboard_error = binding.value;} else{ el._clipboard_message = binding.value;eventUtil.addHandler(el, 'click', => { // log(el._clipboard_message);clipboard(el._clipboard_message).then( msg=> { el._clipboard_success(msg);}).catch( err=> { el._clipboard_error(err);});});}},unbind: function( el, binding) { if(binding.arg === 'success') { deleteel._clipboard_success; } elseif(binding.arg === 'error') { deleteel._clipboard_error; } else{ deleteel._clipboard_message; eventUtil.removeHandler(el, 'click'); }}}
結語
將一些操作 DOM 的方法改為指令方式使用,在 Vue 中可以大大減輕工作量
將一些拓展性高的自定義指令打包到npm上,多個項目都可以使用,減少Ctrl C代碼,且可維護性高,也方便有相同需求的同學可以直接使用
文章來源: https://twgreatdaily.com/zh-hk/QtmxoXIBfGB4SiUw-dTY.html