Vue的指令中實(shí)現(xiàn)傳遞更多參數(shù)
概要
我們?cè)谑褂肰ue的開(kāi)發(fā)項(xiàng)目中,經(jīng)常用自定義指令(directive)來(lái)封裝一系列的DOM操作,這樣做非常方便。一般來(lái)說(shuō),指令是使用動(dòng)態(tài)指令參數(shù)來(lái)獲取App中的數(shù)據(jù)。
但是有些時(shí)候,自定義指令需要更多的數(shù)據(jù)來(lái)完成更復(fù)雜的功能,例如在指令中調(diào)用當(dāng)前App實(shí)例的nextTick方法,以確保所有DOM元素加載完成,再進(jìn)行DOM操作。還有一些情況,我們需要將一些全局配置參數(shù)傳遞給指令,已有的參數(shù)專(zhuān)遞方式,顯然無(wú)法滿(mǎn)足這些需求。
本文介紹一種擴(kuò)展指令參數(shù)的方法,使其可以接收更多參數(shù)。該方法在Vue 2.0和 Vue 3.0中,都可以正常使用。
基本原理
本文介紹的指令擴(kuò)展方法,主要以閉包為基礎(chǔ),并且使用了一些函數(shù)參數(shù)柯里化的方式來(lái)管理多個(gè)參數(shù)的傳遞過(guò)程。
我們以Vue2.0的指令定義方式為例,說(shuō)明基本原理。本文所使用的指令定義方式,都已基于插件化的定義方式,在main.js中,通過(guò)use方法使用。
示例代碼如下:
const myDirective = { ? ? install(app,options){ ? ? ? ? app.directive("img-load", { ? ? ? ? ? ? bind:function(el,binding,vnode){ }, ?? ??? ? ? ?inserted:function(el,binding,vnode){ }, ?? ??? ? ? ?update:function(el,binding,vnode){ }, ?? ??? ? ? ?componentUpdated:function(el,binding,vnode){ }, ?? ??? ? ? ?unbind:function(el,binding,vnode){ }, ? ? ? ? }); ? ? } }; export default myDirective ;
按照上述標(biāo)準(zhǔn)的指令定義方式,無(wú)論使用哪個(gè)鉤子函數(shù),我們只能傳遞三個(gè)參數(shù),指令所綁定的DOM元素,指令接收的APP中綁定參數(shù)和虛擬節(jié)點(diǎn)。
基于閉包的擴(kuò)展方案
指令的鉤子函數(shù)參數(shù)已經(jīng)固定,我們無(wú)法修改。但是我們可以通過(guò)閉包設(shè)置鉤子函數(shù)的作用域,讓閉包函數(shù)來(lái)接收更多參數(shù)。
代碼如下:
export default function getMyDirective(Vue) { ? ? return class MyDirective{ ? ? ? ? constructor(options) { ? ? ? ? ? ? this.options = options; ? ? ? ? ? ? this.bindDirective= this.bindDirective.bind(this); ? ? ? ? } ? ? ? ? bindDirective(el, bindings) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? } } const myDirective = { ? ? install(app,options){ ? ? ??? ?const DirectiveClass = getMyDirective(app) ; ? ? ? ? var myDirective = new DirectiveClass(options); ? ? ? ? app.directive("my-dirctive", { ? ? ? ? ? ? bind:myDirective.bindDirective ? ? ? ? }); ? ? } };
- 使用閉包函getMyDirective來(lái)包裹鉤子函數(shù)bindDirective
- 閉包函數(shù)是用戶(hù)自定義函數(shù),我們可以設(shè)置任意多個(gè)參數(shù)
- 在閉包函數(shù)中定義類(lèi)來(lái)封裝指令的所有操作,構(gòu)造方法也可以接收參數(shù),從而將多個(gè)參數(shù)柯里化分割。
- 通過(guò)bind方法強(qiáng)行將指令鉤子函數(shù)綁定的bindDirective方法的this限定為MyDirective的實(shí)例,也就是說(shuō),bindDirective方法可以通過(guò)this訪問(wèn)更多的數(shù)據(jù)。
JS中函數(shù)具有獨(dú)立作用域,所以指令的綁定方法bindDirective在執(zhí)行過(guò)程中,可以在不受任何外界其他代碼的干擾下,使用閉包函數(shù)傳遞的參數(shù)。
實(shí)例和代碼實(shí)現(xiàn)
本文以一個(gè)圖片自動(dòng)加載的指令為例,介紹自定義指令的參數(shù)擴(kuò)展方式。
自定義指令的基本功能是根據(jù)圖片的URL地址加載并顯示圖片,具體實(shí)現(xiàn)包括:
- 通過(guò)指令動(dòng)態(tài)參數(shù)獲取圖片地址
- 首先在頁(yè)面中顯示一個(gè)正在加載的圖片
- 加載指定地址圖片,如果加載成功,正常顯示
- 加載失敗,顯示一張加載出錯(cuò)的圖片
本文以自頂向下的方式來(lái)介紹該實(shí)例的代碼實(shí)現(xiàn)
Main.js中將指令對(duì)應(yīng)的插件全局化
使用use方法,在全局定義插件ImageLoad,該插件主要是功能是在全局定義一個(gè)圖片加載指令,為該指令接收一個(gè)全局配置,即加載中圖片地址和加載失敗的圖片地址。
Vue.use.use(ImageLoad, { ? loading: "http://localhost:4000/images/loading.gif", ? error: "http://localhost:4000/images/error.jpeg", });
ImageLoad插件定義
ImageLoad插件和其他插件一樣,既然要通過(guò)use使用,所以要定義install方法,install方法的第一個(gè)參數(shù)是當(dāng)前App實(shí)例,第二個(gè)則是指令的全局配置。
import getImageLoad from './getImageLoad' const ImageLoad = { ? ? install(app,options){ ? ? ? ? const ImgClass = getImageLoad(app) ; ? ? ? ? var loadImage = new ImgClass(options); ? ? ? ? app.directive("img-load", { ? ? ? ? ? ? bind: loadImage.bindImage ? ? ? ? }); ? ? } }; export default ImageLoad;
- install方法中,首先通過(guò)調(diào)用getImageLoad方法,獲取加載圖片的管理類(lèi),傳入當(dāng)前App實(shí)例。
- 實(shí)例化圖片加載管理類(lèi)的對(duì)象loadImage ,傳入圖片加載的全局配置。
- 定義自定義指令v-img-load,該指定的bind鉤子方法指向loadImage中的bindImage方法。
- bindImage方法的this是指向loadImage對(duì)象,因此可以使用到App實(shí)例,指令全局配置,loadImage對(duì)象內(nèi)的數(shù)據(jù)。
圖片加載管理類(lèi)的定義
ImageLoadManagement定義了v-img-load指令的全部實(shí)現(xiàn)。
export default function getImageLoad(Vue) { ? ? return class ImageLoadManagement { ? ? ? ? constructor(options) { ? ? ? ? ? ? this.options = options; ? ? ? ? ? ? this.bindImage = this.bindImage.bind(this); ? ? ? ? ? ? this.renderImage = this.renderImage.bind(this); ? ? ? ? } ? ? ? ? bindImage(el, bindings) { ? ? ? ? ? ? const self = this; ? ? ? ? ? ? Vue.nextTick(function(){ ? ? ? ? ? ? ? ? const src = bindings.value; ? ? ? ? ? ? ? ? self.renderImage('loading', src, el); ? ? ? ? ? ? ? ? self.loadImage(src).then( ? ? ? ? ? ? ? ? ? ? () => self.renderImage('', src, el), ? ? ? ? ? ? ? ? ? ? () => self.renderImage('error', src, el), ? ? ? ? ? ? ? ? ); ? ? ? ? ? ? }); ? ? ? ? ? ?? ? ? ? ? } ? ? ? ? loadImage(src) { ? ? ? ? ? ? return new Promise((resolve, reject) => { ? ? ? ? ? ? ? ? const img = new Image(); ? ? ? ? ? ? ? ? img.src = src; ? ? ? ? ? ? ? ? img.onload = resolve; ? ? ? ? ? ? ? ? img.onerror = reject; ? ? ? ? ? ? }); ? ? ? ? } ? ? ? ? renderImage(type, src, el) { ? ? ? ? ? ? let _src; ? ? ? ? ? ? const { ? ? ? ? ? ? ? ? error, ? ? ? ? ? ? ? ? loading ? ? ? ? ? ? } = this.options; ? ? ? ? ? ? switch (type) { ? ? ? ? ? ? ? ? case 'loading': ? ? ? ? ? ? ? ? ? ? _src = loading; ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? case 'error': ? ? ? ? ? ? ? ? ? ? _src = error; ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? default: ? ? ? ? ? ? ? ? ? ? _src = src; ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? ? el.setAttribute("src", _src); ? ? ? ? } ? ? } }
為了避免參數(shù)過(guò)多,所以采用柯里化的方法,對(duì)參數(shù)進(jìn)行了分割:
- 在閉包函數(shù)getImageLoad中,定義了全局App實(shí)例參數(shù);
ImageLoadManagement
類(lèi)的構(gòu)造方法中定義了圖片加載指令需要的全局配置參數(shù)。class
作為function的語(yǔ)法糖使用,其本質(zhì)來(lái)時(shí)function,從而實(shí)現(xiàn)獨(dú)立作用域。bindImage
方法中可以直接使用App實(shí)例的nextTick,無(wú)論該指令在父組件還是子組件中使用,都可以保證在指令中代碼執(zhí)行時(shí),所有DOM元素加載完成。loadImage
方法用于檢查指定URL的圖片是否存在,如果存在則顯示具體圖片,否則則顯示加載失敗的圖片。renderImage
方法用于設(shè)置指令綁定的Img元素的圖片地址,圖片的實(shí)際地址可以通過(guò)bindings參數(shù)的value屬性獲取。
通過(guò)上述方法,我們不僅擴(kuò)展了指令的參數(shù),使其可以支持更復(fù)雜的業(yè)務(wù)邏輯。
更重要的是。我們實(shí)現(xiàn)了指令的定義和實(shí)現(xiàn)邏輯的解耦,完全不再需要將所有的指令實(shí)現(xiàn)邏輯全部放在指令的注冊(cè)方法中。通過(guò)ImageLoadManagement 的定義,將所有的指令實(shí)現(xiàn)邏輯都內(nèi)聚在其中。
Vue 3.0的實(shí)現(xiàn)
Vue 3.0中,指令參數(shù)的擴(kuò)展方法思路與2.0一致,只是因?yàn)閂ue 3.0中指令的鉤子函數(shù)名稱(chēng)與2.0不一致,造成一些區(qū)別。
具體代碼如下:
import getImageLoad from './getImageLoad' const ImageLoad = { install(app,options){ const ImgClass = getImageLoad(options) ; var loadImage = new ImgClass(); app.directive("img-load", { mounted: loadImage.bindImage }); } }; export default ImageLoad;
export default function getImageLoad(options) { return class ImageLoadManagement { constructor() { this.options = options; this.bindImage = this.bindImage.bind(this); this.renderImage = this.renderImage.bind(this); } bindImage(el, bindings) { const src = bindings.value; this.renderImage('loading', src, el); this.loadImage(src).then( () => this.renderImage('', src, el), () => this.renderImage('error', src, el), ); } loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); img.src = src; img.onload = resolve; img.onerror = reject; }); } renderImage(type, src, el) { let _src; const { error, loading } = this.options; switch (type) { case 'loading': _src = loading; break; case 'error': _src = error; break; default: _src = src; break; } el.setAttribute("src", _src); } } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue使用Element-UI生成并展示表頭序號(hào)的方法
序號(hào)算是在展示數(shù)據(jù)的時(shí)候,一種很普遍的屬性了,我們可以自己寫(xiě)生成序號(hào)的規(guī)則,也可以借助第三方,這篇文章主要介紹了Vue使用Element-UI生成并展示表頭序號(hào)的方法,需要的朋友可以參考下2023-01-01使用Element實(shí)現(xiàn)表格表頭添加搜索圖標(biāo)和功能
這篇文章主要介紹了使用Element實(shí)現(xiàn)表格表頭添加搜索圖標(biāo)和功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07Vue+elementUI實(shí)現(xiàn)多圖片上傳與回顯功能(含回顯后繼續(xù)上傳或刪除)
這篇文章主要介紹了Vue+elementUI實(shí)現(xiàn)多圖片上傳與回顯功能(含回顯后繼續(xù)上傳或刪除),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03Vue Element前端應(yīng)用開(kāi)發(fā)之組織機(jī)構(gòu)和角色管理
本篇文章繼續(xù)深化Vue Element權(quán)限管理模塊管理的內(nèi)容,介紹組織機(jī)構(gòu)和角色管理模塊的處理,使得我們了解界面組件化模塊的開(kāi)發(fā)思路和做法,提高我們界面設(shè)計(jì)的技巧,并減少代碼的復(fù)雜性,提高界面代碼的可讀性,同時(shí)也是利用組件的復(fù)用管理。2021-05-05詳解10分鐘學(xué)會(huì)vue滾動(dòng)行為
本篇文章主要介紹了vue滾動(dòng)行為,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09vue+elementUI組件遞歸實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航
這篇文章主要介紹了vue+elementUI組件遞歸實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Vue動(dòng)態(tài)組件?component?:is的使用代碼示范
vue?動(dòng)態(tài)組件用于實(shí)現(xiàn)在指定位置上,動(dòng)態(tài)加載不同的組件,這篇文章主要介紹了Vue動(dòng)態(tài)組件?component?:is的使用,需要的朋友可以參考下2023-09-09Vue實(shí)例中生命周期created和mounted的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于Vue實(shí)例中生命周期created和mounted區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08vue如何設(shè)置動(dòng)態(tài)的柵格占位、水平偏移量、類(lèi)名、樣式
這篇文章主要介紹了vue如何設(shè)置動(dòng)態(tài)的柵格占位、水平偏移量、類(lèi)名、樣式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09