當(dāng)你第一次接觸vue的時(shí)候,一定會(huì)使用到其中幾個(gè)指令,比如:v-if、v-for、v-bind...這些都是vue為我們寫(xiě)好的,用起來(lái)相當(dāng)?shù)乃?。如果有些?chǎng)景不滿(mǎn)足,需要我們自己去自定義,那要怎么辦呢?下面來(lái)一步步學(xué)習(xí)如何自定義一個(gè)屬性我們自己的指令。
什么是vue指令?
指令是可以寫(xiě)在DOM元素的小命令,他們以v-為前綴,vue就能識(shí)別這是一個(gè)指令并保持語(yǔ)法的一致性。如果你需要對(duì)HTML進(jìn)行底層操作的話(huà),這種方式是非常有用。
下面介紹幾種指令的使用方式及示例,用example代替了實(shí)際的指令。
v-example:實(shí)例化一個(gè)指令,但這個(gè)指令沒(méi)有參數(shù)。如果不傳參數(shù)會(huì)比較不靈活,但是這樣就已經(jīng)操作DOM元素的能力了。
v-example=“value”:這樣可以傳值到指令中,指令會(huì)根據(jù)value值來(lái)操作html
<div v-if="stateExample">stateExample為true時(shí)會(huì)顯示</div>
v-example="string":使用字符串作為表達(dá)式。
<p v-html="'<strong>this is an example of a string in some text <strong>'"></p>
v-example:arg="value":這里可以傳參數(shù)(arg),在下面的例子中,我們綁定一個(gè)class,然后給這個(gè)class設(shè)置樣式
<div v-bind:class="someClassObject"></div>
v-example:arg.modifier="value":使用修飾符(modifier),下面的例子可以在click事件上調(diào)用preventDefault()。
<button v-on:submit.prevent="onSubmit"></button>
vue自定義指令的基礎(chǔ)知識(shí)
現(xiàn)在對(duì)指令有了大概的了解后,我們?cè)賮?lái)學(xué)習(xí)一下如何創(chuàng)建一個(gè)自定義指令。
鉤子函數(shù)
一個(gè)指令定義對(duì)象可以提供如下幾個(gè)鉤子函數(shù)(均為可選)
bind:只調(diào)用一次,指令第一次綁定到元素時(shí)調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。
inserted:被綁定元素插入父節(jié)點(diǎn)時(shí)調(diào)用(僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。
update:所在組件中的VNode更新時(shí)調(diào)用,但是可能發(fā)生在其子VNode更新之前。指令的值可能發(fā)生了改變,也可能沒(méi)用。但是你可以通過(guò)比較更新簽后的值來(lái)忽略不必要的模板更新
componentUpdated:指令所在組件的VNode及其子VNode全部更新后調(diào)用。
unbind:只調(diào)用一次,指令與元素解綁時(shí)調(diào)用。
接下來(lái)我們來(lái)看一下鉤子函數(shù)的參數(shù)(即el、binding、vnode和oldVnode)。
鉤子函數(shù)參數(shù)
指令鉤子函數(shù)會(huì)被傳入以下參數(shù):
el:指令所綁定的元素,可以用來(lái)直接操作DOM。
binding:一個(gè)對(duì)象,包含以下屬性:
name:指令名,不包括v-前綴。
value:指令的綁定值,例如v-my-directive="1+1"中,綁定之為2
oldValue:指令綁定的前一個(gè)值,僅在update和componentUpdated鉤子中可用。無(wú)論值是否改變都可用。
expression:字符串形式的指令表達(dá)式。例如v-my-directive:"1+1"中,表達(dá)式為“1+1”.
args:傳給指令的參數(shù),可選。例如v-my-directive:foo中,參數(shù)為“foo”
modifiers:一個(gè)包含修飾符的對(duì)象。例如:v-my-directive.foo.bar中,修飾符對(duì)象為{foo:true,bar:true}
vnode:Vue編譯的虛擬節(jié)點(diǎn)。移步VNodeAPI來(lái)了解更多詳情
oldVnode:上一個(gè)虛擬節(jié)點(diǎn),僅在update和componentUpdated鉤子中可用。
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo',{
bind:function(el,binding,vnode){
var s=JSON.stringify
el.innerHTML=
"name:"+s(binding.name)+'<br>'+
'value:'+s(binding.value)+'<br>+
'expression:''+s(binding.expression)+'<br>'+
"argument:"+s(binding.arg)+'<br>'+
'vnode keys:'+Object.keys(vnode).join(",")+
}
})
new Vue({
el:"#hook-arguments-example,
data:{
message:'hello!'}
})
函數(shù)簡(jiǎn)寫(xiě)
在很多時(shí)候,你可能想在bind和update時(shí)觸發(fā)相同行為,而不關(guān)心其他的鉤子。比如這樣寫(xiě):
Vue.directive('color-swatch',function(el,binding){
? ? el.style.backgroundColor=binding.value
})
對(duì)象字面量
如果指令需要多個(gè)值,可以傳入一個(gè)JavaScript對(duì)象字面量。記住,指令函數(shù)能夠接受所有的合法的JavaScript表達(dá)式
<div v-demo="{color:'white',text:'hello'}"></div>
Vue.directive('demo',function(el,binding){
console.log(binding.value.color)//=>"white"
console.log(binding.value.text)//=>"hello!"
})
vue自定義指令的使用場(chǎng)景
在Vue2.0中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對(duì)普通dom元素進(jìn)行底層操作,這時(shí)候就會(huì)用到自定義指令。下面就來(lái)看看vue自定義指令有哪些使用場(chǎng)景。
Vue.directive('focus',{
inserted:function(el){
?el.focus();
}
})
一鍵copy的功能
1.首先建一個(gè)js文件(v-copy.js)。定義一個(gè)對(duì)象。(指令實(shí)際上就是一個(gè)對(duì)象)
important {Message} from 'ant-design-vue';
const vCopy={
/*
bind鉤子函數(shù),第一次綁定時(shí)調(diào)用,可以在這里做初始化設(shè)置
el:作用的dom對(duì)象
value:傳給指令的值,也就是我們要copy的值
}*/
bind(el,{value}){
?el.$value=value;//用一個(gè)全局屬性來(lái)存進(jìn)來(lái)的值,因?yàn)檫@個(gè)值在別的鉤子函數(shù)還會(huì)用到
el.handler=()=>{
if(!el.$value){
//值為空的時(shí)候,給出提示,我這里的提示是用的ant-design-vue的提示,你們隨意
Message.warning("無(wú)復(fù)制內(nèi)容");
return;
}
const textarea=document.createElement('textarea');
textarea.readOnly='readonly';
textarea.style.position='absolute';
textarea.style.left="-9999px";
//將要copy的值賦給textarea標(biāo)簽的value屬性
textarea.value=el.$value;
document.body.appendChild(textarea);
//選中值并復(fù)制
textarea.select();
const result=document.execCommand('Copy');
if(result){
Message.success("復(fù)制成功");
}
document.body.removeChild(textarea);
}
el.addEventListener('click',el.handler);
},
componentUpdated(el,{value}){
el.$value=value;
},
//指令與元素解綁的時(shí)候,移除事件綁定
unbind(el){
el.removeEventListener('click',el.handler);
}
export default vCopy
2.到這里,一鍵Copy的功能就實(shí)現(xiàn)了,最后再說(shuō)一嘴怎么將自定義指令注冊(cè)到全局:再新建一個(gè)js(directives.js)文件文件來(lái)注冊(cè)所有的全局指令。
import copy from './v-copy';
//自定義指令
const directives={
copy,
}
//這種寫(xiě)法可以批量注冊(cè)指令
export default{
install(Vue){
Object.keys(directives).forEach(key)=>{
Vue.directive(lkey,directives[key]);
});
};
}
3.最后,在main.js中這樣引入:
important Vue from 'vue';
important Directives from"./directives";
Vue.use(Directives);
按鈕級(jí)別權(quán)限控制
權(quán)限控制分為頁(yè)面級(jí)別和按鈕級(jí)別,這兩種思路基本是一致的。
頁(yè)面級(jí)別:用戶(hù)登錄后,獲取用戶(hù)role,將role和路由表每個(gè)頁(yè)面的需要的權(quán)限作比較,生成最終用戶(hù)可訪(fǎng)問(wèn)的路由表。最后通過(guò)router.addRoutes動(dòng)態(tài)掛載?,F(xiàn)在是通過(guò)獲取到用戶(hù)的role之后,在前端用v-if手動(dòng)判斷來(lái)區(qū)分不同權(quán)限對(duì)應(yīng)的按鈕的。。
按鈕級(jí)別:用戶(hù)登錄后,獲取用戶(hù)role,在前端用v-if或者封裝一個(gè)自定義指令,手動(dòng)判斷來(lái)區(qū)分不同權(quán)限對(duì)應(yīng)的按鈕的思路:
登錄:當(dāng)用戶(hù)填寫(xiě)賬號(hào)和密碼后向服務(wù)器驗(yàn)證是否正確,驗(yàn)證通過(guò)之后,服務(wù)端回返回一個(gè)token,拿到token之后(將token存在sessionStorage中,保證刷新新頁(yè)面后能記住用戶(hù)登錄狀態(tài)),前端會(huì)根據(jù)token再去拉取一個(gè)user_info的接口來(lái)獲取用戶(hù)的詳細(xì)信息(如用戶(hù)角色,用戶(hù)權(quán)限,用戶(hù)名等等信息)。
權(quán)限驗(yàn)證:通過(guò)token獲取用戶(hù)對(duì)應(yīng)的role,自定義指令,獲取路由meta屬性里btnPermissions(注:meta.btnPermissions是存放按鈕權(quán)限的數(shù)組,在路由表里配置),然后判斷role是否在btnPermission數(shù)組里,若不再即刪除該按鈕DOM。
路由配置:
path:'/permission',
component:Layout,
name:'權(quán)限測(cè)試',
meta:{btnPermissions:['admin','super','normal']},//頁(yè)面需要的權(quán)限
children:[
{
?path:'supper',
component:_import('system/supper'),
name:'權(quán)限測(cè)試頁(yè)',
meta:{btnPermissions:['admin','supper']}//頁(yè)面需要權(quán)限,
},
{
path:'normal',
component:_import('system/normal'),
name:'權(quán)限測(cè)試頁(yè)',
meta:{btnPermissions:['admin']}//頁(yè)面需要的權(quán)限
}]
import Vue from "vue"
const has=Vue.directive('has',{
bind:function(el,binding,vnode){
let btnPermissionArr=vnode.context.$route.meta.btnPermissions;
if(!Vue.prototype.$_has(btnPermissionArr)){
el.parantNode.removeChild(el);
}
}
})
Vue.prototype.$_has=function(value){
let isExist=false;
//獲取用戶(hù)按鈕權(quán)限
let btnPermissionStr=sessionStorage.getItem("btnPermissions");
if(btnPermissionStr==undefined||btnPermissionStr==null){
return false;
}
if(value.indexOf(btnPermissionsStr)>-1){
isExist=true;
}
return isExist;
};
export {has}
然后在main.js文件引入文件
important has from "./public/js/btnPermissions.js";
頁(yè)面中按鈕只需加v-has即可
<el-button @click="editClick" type="primary" v-has>編輯</el-button>