[譯文]Vue: 強大的自定義指令
當你最初學習JavaScript框架的時候, 有點像一個小孩在一個糖果商店裡面. 你接受一切對於你來說可用的東西, 它會讓我們的開發變得更簡單. 儘管如此, 我們都有一個經驗: 框架不是萬能的, 不可能全面覆蓋各種各樣的情況.
令人興奮的是, Vue的功能令人難以置信的豐富. 儘管有可能有些特殊情況沒有被Vue框架本身所覆蓋, 但是你可以非常快速輕鬆的創建自定義指令.
什麼是指令?
我曾經在我的Vue.js入門裡面寫過一篇關於指令的文章, 現在不妨讓我們回顧一下:
指令只一種可以附加到DOM元素的微命令(tiny commands). 它們通常以"v-"作為前綴, 以方便Vue知道你在使用一種特殊的標記, 從而確保語法的一致性. 如果你需要對HTML元素的低級別(low-level)訪問來控制一些行為, 它們通常很有用.
如果你正在使用Vue(或者Angular), 你可能已經很熟悉其中的一些指令, 如: v-if, v-else等等. 我們將從了解一些基礎開始, 但是如果你更願意直接看例子, 請直接往下滾動. 這些例子也能很好的讓你理解這些概念.
以下是一些指令的使用方法, 以及對應的例子片段. 這些例子不是規定性的, 它們只是一些用例. 這裡的"例子"實際上是"指令".
v-example: 這將實例化一個指令, 但不接受任何參數. 雖然不帶參數的指令, 在使用的過程中並不是很靈活, 但是你仍然可以通過這種類型的指令對DOM元素做一些操作.
v-example="value": 這將傳遞一個值給指令, 並且該指令根據該值計算出要做的操作.
<div v-if="stateExample">I will show up if stateExample is true</div>
v-example="string": 這將讓你把string作為一個表達式.
<p v-html="<strong>this is an example of a string in some text</strong>"></p>
v-example:arg="value": 這允許我們傳入一個參數給指令. 下面的例子中, 我們綁定到一個類, 將其樣式化為一個對象, 單獨存儲.
<div v-bind:class="someClassObject"></div>
v-example:arg.modifier="value": 這允許我們使用修飾語. 下面的例子中, 允許我們在點擊事件時, 調用preventDefault().
<button v-on:submit.prevent="onSubmit"></button>
領悟自定義指令
既然我們已經大致過了一遍所有的我們所用過的指令類型方法, 讓我們想一想我們如何通過自己編寫的自定義指令來實現它們? 使用自定義指令的一個很好的例子是滾動事件, 讓我們看看如何實現它.
首先, 最基本的是如何創建一個全局的指令. (是的, 它什麼也不做.) 僅僅是創建了一個指令.
Vue.directive(tack);
HTML:
<p v-tack>This element has a directive on it</p>
我們有幾個可用的鉤子, 每個鉤子可以選擇一些參數. 鉤子如下:
- bind: 一旦指令附加到元素時觸發
- inserted: 一旦元素被添加到父元素時觸發
- update: 每當元素本身更新(但是子元素還未更新)時觸發
- componentUpdate: 每單組件和子組件被更新時觸發
- unbind: 一旦指令被移除時觸發
每個鉤子都有el, binding, 和vnode參數可用. update和componentUpdated鉤子還暴露了oldVnode, 以區分傳遞的舊值和較新的值.
el, 跟你所期待的一樣, 就是所綁定的元素. binding是一個保護傳入鉤子的參數的對象. 有很多可用的參數, 包括name, value, oldValue, expression, arguments, arg及修飾語. vnode有一個更不尋常的用例, 它可用於你需要直接引用到虛擬DOM中的節點. binding和vnode都應該被視為只讀.
綁定一個自定義指令
既然我們已經知道了這一點, 就可以開始研究如何在實際中使用一個自定義指令. 讓我們完善剛才所創建的第一個指令, 讓它變得有用:
Vue.directive(tack, { bind(el, binding, vnode) { el.style.position = fixed }});
在HTML元素中:
<p v-tack>I will now be tacked onto the page</p>
毫無疑問, 它完全可以按照我們所希望的工作. 但是它還不夠靈活, 如果我們可以傳入一個值, 然後直接更新或者重用這個指令就好了. 例如, 我們想為這個元素指定一個值, 表示這個元素離頂部多遠(多少個像素), 我們可以這樣寫(在CODEPEN上查看):
// JSVue.directive(tack, { bind(el, binding, vnode){ el.style.position = fixed; el.style.top = binding.value + px; }});// HTML<div id="app"> <p>Scroll down the page</p> <p v-tack="70">Stick me 70px from the top of the page</p></div>
假設我們想要區分從頂部或者左側偏移70px, 我們可以通過傳遞一個參數來做到這一點(在CODEPEN上查看):
// JSVue.directive(tack, { bind(el, binding, vnode) { el.style.position = fixed; const s = (binding.arg === left ? left : top); el.style[s] = binding.value + px; }});// HTML<p v-tack:left="70">Ill now be offset from the left instead of the top</p>
當然, 你可以同時傳入不止一個值. 你可以像使用標準指令一樣簡單的使用自定義指令(在CODEPEN上查看):
// JSVue.directive(tack, { bind(el, binding, vnode) { el.style.position = fixed; el.style.top = binding.value.top + px; el.style.left = binding.value.left + px; }});// HTML<p v-tack="{top: 40, left: 100}">Stick me 40px from the top of the page and 100px from the left of the page</p>
基於我們的自定義指令, 我們可以創建和修改方法, 從而創建更為複雜的自定義指令. 這裡, 我們將做一個waypoints-like例子, 用少量的代碼實現特定滾動事件觸發的動畫效果(在CODEPEN上查看):
// JSVue.directive(scroll, { inserted: function(el, binding) { let f = function(evt) { if(binding.value(evt, el)) { window.removeEventListener(scroll, f); } }; window.addEventListener(scroll, f); }});// main appnew Vue({ el: "#app", methods: { handleScroll: function(evt, el) { if(window.scrollY > 50) { TweenMax.to(el, 1.5, { y: -10, opacity: 1, ease: Sine.easeOut }); } return window.scrollY > 100; } }});// HTML<div class="box" v-scroll="handleScroll"> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A atque amet harum aut ab veritatis earum porro praesentium ut corporis. Quasi provident dolorem officia iure fugiat, eius mollitia sequi quisquam.</p></div>
為了讓大家看得更清楚, 在這個代碼片段中, 我們儘可能的保證它的簡單易讀. 在實際的APP中, 你可以構建非常友好的, 並且非常靈活的, 適合整個團隊使用的自定義指令.
在實際的構建中, 我會將指令代碼放在"main.js"文件中, 該文件位於"src"目錄的根目錄下(如果你使用的是Vue-cli構建工具), 那麼"App.vue"以及組件目錄中的所有的.vue文件都可以訪問它. 當然, 還要其他方法可以使用它, 但是我發現對於整個應用程序來說, 這是最靈活的實現方式.
如果你希望了解更多的關於Vue框架的東西, 歡迎點擊這裡.
推薦閱讀:
※51單片機指令
※特斯拉CEO馬斯克:5年後,機器人將會屠殺人類?可能性有多大?
※模聯上你見過哪些奇葩指令?
※虛擬機位元組碼解釋哪種模式速度更快?