在使用 Element 組件庫時,有些需求功能是組件庫不帶有的,使用了自定義指令來解決這些問題
代碼倉庫vue-element-utils
也提供了 npm 包下載使用css
yarn add vue-element-utils npm install vue-element-utils
入口文件引入:vue
import elementUtils from 'vue-element-utils' Vue.use(elementUtils);
例如:node
Vue 提供了內置指令v-model
,v-show
等,也容許註冊自定義指令。多數狀況中代碼的複用和抽象的主要形式是組件。有的狀況你仍然須要對普通 DOM 元素進行底層操做,這時候就會用到自定義指令。
bind
: 只調用一次,指令第一次綁定到元素時調用。這裏能夠進行一次性的初始化設置inserted
: 被綁定元素插入父節點時調用(僅保證父節點存在,但不必定已被插入文檔中)update
: 所在組件的 VNode 更新時調用componentUpdated
: 所在組件的 VNode 及其子 VNode 所有更新後調用unbind
: 只調用一次,指令與元素解綁時調用
除了
el
以外,其它參數都應該是隻讀的。若是須要在鉤子之間共享數據,建議經過元素的
dataset來進行。
el
: 指令所綁定的元素,能夠用來直接操做 DOMbinding
: 一個對象,包含如下 property:git
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 組件須要展現的數據很是多的時候,滾動加載能夠做爲一種優化手段,固然還有其它更好的方式,能夠參考Vue項目優化總結。github
展現:express
export default { bind(el, binding) { // 獲取 element-ui 定義好的 scroll 盒子 const selectWrap = el.querySelector( '.el-select-dropdown .el-select-dropdown__wrap' ); selectWrap.addEventListener('scroll', function() { /** * scrollHeight 獲取元素內容高度(只讀) * * scrollTop 獲取或者設置元素的偏移量,經常使用於:計算滾動條的位置,當一個元素的容器沒有產生方向的滾動條,那它的 scrollTop 的值默認爲0 * * clientHeight 讀取元素的可見高度(只讀) * * 若是元素滾動到底, 等式`ele.scrollHeight - ele.scrollTop === ele.clientHeight;`返回 true, 沒有則返回 false */ const condition = this.scrollHeight - this.scrollTop <= this.clientHeight; condition && binding.value(); }); } }
展現:npm
export default { bind(el) { const dialogHeaderEl = el.querySelector('.el-dialog__header'); const dragDom = el.querySelector('.el-dialog'); dialogHeaderEl.style.cssText += ';cursor:move;'; // 獲取原有屬性 ie dom 元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null); dialogHeaderEl.onmousedown = (e) => { // 鼠標按下,計算當前元素距離可視區的距離 const disX = e.clientX - dialogHeaderEl.offsetLeft; const disY = e.clientY - dialogHeaderEl.offsetTop; // 獲取到的值帶 px 正則匹配替換 let styL, styT; // 注意在 ie 中 第一次獲取到的值爲組件自帶 50% 移動以後賦值爲 px if (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) { // 經過事件委託,計算移動的距離 const l = e.clientX - disX; const t = 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; }; }; } }
展現:element-ui
export default { bind(el) { const dragDom = el.querySelector('.el-dialog'); const lineEl = document.createElement('div'); lineEl.style = 'width: 2px; background: inherit; height: 80%; position: absolute; right: 0; top: 0; bottom: 0; margin: auto; z-index: 1; cursor: w-resize;'; lineEl.addEventListener( 'mousedown', function(e) { // 鼠標按下,計算當前元素距離可視區的距離 const disX = e.clientX - el.offsetLeft; // 當前寬度 const curWidth = dragDom.offsetWidth; document.onmousemove = function(e) { e.preventDefault(); // 移動時禁用默認事件 // 經過事件委託,計算移動的距離 const l = e.clientX - disX; dragDom.style.width = `${curWidth + l}px`; }; document.onmouseup = function(e) { document.onmousemove = null; document.onmouseup = null; }; }, false ); dragDom.appendChild(lineEl); } }
展現:app
const eventUtil = { addHandler(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, handler); } else { element['on' + type] = handler; } }, removeHandler(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } } }; function clipboard(text) { return new Promise((reslove, reject) => { const input = document.createElement('input'); input.setAttribute('readonly', 'readonly'); input.setAttribute('value', text); document.body.appendChild(input); input.focus(); input.setSelectionRange(0, 9999); if (document.execCommand('copy')) { document.execCommand('copy'); reslove(text); } else { reject(new Error('複製失敗')); } document.body.removeChild(input); }); } const $clipboard = clipboard; export { $clipboard } export default { bind: function(el, binding, vnode) { if (binding.arg === 'success') { el._clipboard_success = binding.value; } else if (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') { delete el._clipboard_success; } else if (binding.arg === 'error') { delete el._clipboard_error; } else { delete el._clipboard_message; eventUtil.removeHandler(el, 'click'); } } }
將一些操做 DOM 的方法改成指令方式使用,在 Vue 中能夠大大減輕工做量dom
將一些拓展性高的自定義指令打包到npm上,多個項目均可以使用,減小Ctrl C
代碼,且可維護性高,也方便有相同需求的同窗能夠直接使用