vue中如何自定義指令directive

一波附、 什么是自定義指令

我們看到的v-開頭的行內屬性诈火,都是指令,不同的指令可以完成或實現不同的功能灵妨,對普通 DOM元素進行底層操作解阅,這時候就會用到自定義指令落竹。除了核心功能默認內置的指令 (v-model 和 v-show)泌霍,Vue 也允許注冊自定義指令

指令使用的幾種方式:

//會實例化一個指令,但這個指令沒有參數 
`v-xxx`
 
// -- 將值傳到指令中
`v-xxx="value"`  
 
// -- 將字符串傳入到指令中述召,如`v-html="'<p>內容</p>'"`
`v-xxx="'string'"` 
 
// -- 傳參數(`arg`)朱转,如`v-bind:class="className"`
`v-xxx:arg="value"` 
 
// -- 使用修飾符(`modifier`)
`v-xxx:arg.modifier="value"` 

二、 如何自定義指令

注冊一個自定義指令有全局注冊與局部注冊

全局注冊注冊主要是用過Vue.directive方法進行注冊

Vue.directive第一個參數是指令的名字(不需要寫上v-前綴)积暖,第二個參數可以是對象數據藤为,也可以是一個指令函數

// 注冊一個全局自定義指令 `v-focus`
Vue.directive('focus', {
  // 當被綁定的元素插入到 DOM 中時……
  inserted: function (el) {
    // 聚焦元素
    el.focus()  // 頁面加載完成之后自動讓輸入框獲取到焦點的小功能
  }
})

局部注冊通過在組件options選項中設置directive屬性

directives: {
  focus: {
    // 指令的定義
    inserted: function (el) {
      el.focus() // 頁面加載完成之后自動讓輸入框獲取到焦點的小功能
    }
  }
}

然后你可以在模板中任何元素上使用新的v-focusproperty,如下:

<input v-focus />

鉤子函數

自定義指令也像組件那樣存在鉤子函數:

  • bind:只調用一次夺刑,指令第一次綁定到元素時調用缅疟。在這里可以進行一次性的初始化設置

  • inserted:被綁定元素插入父節(jié)點時調用 (僅保證父節(jié)點存在,但不一定已被插入文檔中)

  • update:所在組件的 VNode 更新時調用遍愿,但是可能發(fā)生在其子 VNode更新之前存淫。指令的值可能發(fā)生了改變,也可能沒有沼填。但是你可以通過比較更新前后的值來忽略不必要的模板更新

  • componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調用

  • unbind:只調用一次桅咆,指令與元素解綁時調用

所有的鉤子函數的參數都有以下:

  • 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 編譯生成的虛擬節(jié)點

`oldVnode`:上一個虛擬節(jié)點,僅在 update 和 componentUpdated 鉤子中可用

除了 el 之外牵署,其它參數都應該是只讀的漏隐,切勿進行修改。如果需要在鉤子之間共享數據奴迅,建議通過元素的 dataset 來進行
舉個例子:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
<script>
    Vue.directive('demo', function (el, binding) {
    console.log(binding.value.color) // "white"
    console.log(binding.value.text)  // "hello!"
    })
</script>

三青责、應用場景

使用自定義組件組件可以滿足我們日常一些場景,這里給出幾個自定義組件的案例:

  • 防抖

  • 圖片懶加載

  • 一鍵 Copy的功能

輸入框防抖

防抖這種情況設置一個v-throttle自定義指令來實現

舉個例子:


// 1.設置v-throttle自定義指令
Vue.directive('throttle', {
  bind: (el, binding) => {
    let throttleTime = binding.value; // 防抖時間
    if (!throttleTime) { // 用戶若不設置防抖時間取具,則默認2s
      throttleTime = 2000;
    }
    let cbFun;
    el.addEventListener('click', event => {
      if (!cbFun) { // 第一次執(zhí)行
        cbFun = setTimeout(() => {
          cbFun = null;
        }, throttleTime);
      } else {
        event && event.stopImmediatePropagation();
      }
    }, true);
  },
});
// 2.為button標簽設置v-throttle自定義指令
<button @click="sayHello" v-throttle>提交</button>

圖片懶加載

設置一個v-lazy自定義組件完成圖片懶加載

const LazyLoad = {
    // install方法
    install(Vue,options){
       // 代替圖片的loading圖
        let defaultSrc = options.default;
        Vue.directive('lazy',{
            bind(el,binding){
                LazyLoad.init(el,binding.value,defaultSrc);
            },
            inserted(el){
                // 兼容處理
                if('InterpObserver' in window){
                    LazyLoad.observe(el);
                }else{
                    LazyLoad.listenerScroll(el);
                }
                
            },
        })
    },
    // 初始化
    init(el,val,def){
        // src 儲存真實src
        el.setAttribute('src',val);
        // 設置src為loading圖
        el.setAttribute('src',def);
    },
    // 利用InterpObserver監(jiān)聽el
    observe(el){
        let io = new InterpObserver(entries => {
            let realSrc = el.dataset.src;
            if(entries[0].isIntersecting){
                if(realSrc){
                    el.src = realSrc;
                    el.removeAttribute('src');
                }
            }
        });
        io.observe(el);
    },
    // 監(jiān)聽scroll事件
    listenerScroll(el){
        let handler = LazyLoad.throttle(LazyLoad.load,300);
        LazyLoad.load(el);
        window.addEventListener('scroll',() => {
            handler(el);
        });
    },
    // 加載真實圖片
    load(el){
        let windowHeight = document.documentElement.clientHeight
        let elTop = el.getBoundingClientRect().top;
        let elBtm = el.getBoundingClientRect().bottom;
        let realSrc = el.dataset.src;
        if(elTop - windowHeight<0&&elBtm > 0){
            if(realSrc){
                el.src = realSrc;
                el.removeAttribute('src');
            }
        }
    },
    // 節(jié)流
    throttle(fn,delay){
        let timer; 
        let prevTime;
        return function(...args){
            let currTime = Date.now();
            let context = this;
            if(!prevTime) prevTime = currTime;
            clearTimeout(timer);
            
            if(currTime - prevTime > delay){
                prevTime = currTime;
                fn.apply(context,args);
                clearTimeout(timer);
                return;
            }
 
            timer = setTimeout(function(){
                prevTime = Date.now();
                timer = null;
                fn.apply(context,args);
            },delay);
        }
    }
 
}
export default LazyLoad;

一鍵 Copy的功能

import { Message } from 'ant-design-vue';
 
const vCopy = { //
  /*
    bind 鉤子函數脖隶,第一次綁定時調用,可以在這里做初始化設置
    el: 作用的 dom 對象
    value: 傳給指令的值暇检,也就是我們要 copy 的值
  */
  bind(el, { value }) {
    el.$value = value; // 用一個全局屬性來存?zhèn)鬟M來的值产阱,因為這個值在別的鉤子函數里還會用到
    el.handler = () => {
      if (!el.$value) {
      // 值為空的時候,給出提示块仆,我這里的提示是用的 ant-design-vue 的提示构蹬,你們隨意
        Message.warning('無復制內容');
        return;
      }
      // 動態(tài)創(chuàng)建 textarea 標簽
      const textarea = document.createElement('textarea');
      // 將該 textarea 設為 readonly 防止 iOS 下自動喚起鍵盤,同時將 textarea 移出可視區(qū)域
      textarea.readOnly = 'readonly';
      textarea.style.position = 'absolute';
      textarea.style.left = '-9999px';
      // 將要 copy 的值賦給 textarea 標簽的 value 屬性
      textarea.value = el.$value;
      // 將 textarea 插入到 body 中
      document.body.appendChild(textarea);
      // 選中值并復制
      textarea.select();
      // textarea.setSelectionRange(0, textarea.value.length);
      const result = document.execCommand('Copy');
      if (result) {
        Message.success('復制成功');
      }
      document.body.removeChild(textarea);
    };
    // 綁定點擊事件悔据,就是所謂的一鍵 copy 啦
    el.addEventListener('click', el.handler);
  },
  // 當傳進來的值更新的時候觸發(fā)
  componentUpdated(el, { value }) {
    el.$value = value;
  },
  // 指令與元素解綁的時候庄敛,移除事件綁定
  unbind(el) {
    el.removeEventListener('click', el.handler);
  },
};
 
export default vCopy;

拖拽

<div ref="a" id="bg" v-drag></div>

  directives: {
    drag: {
      bind() {},
      inserted(el) {
        el.onmousedown = (e) => {
          let x = e.clientX - el.offsetLeft;
          let y = e.clientY - el.offsetTop;
          document.onmousemove = (e) => {
            let xx = e.clientX - x + "px";
            let yy = e.clientY - y + "px";
            el.style.left = xx;
            el.style.top = yy;
          };
          el.onmouseup = (e) => {
            document.onmousemove = null;
          };
        };
      },
    },
  },
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜜暑,隨后出現的幾起案子铐姚,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隐绵,死亡現場離奇詭異之众,居然都是意外死亡,警方通過查閱死者的電腦和手機依许,發(fā)現死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門棺禾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人峭跳,你說我怎么就攤上這事膘婶。” “怎么了蛀醉?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵悬襟,是天一觀的道長。 經常有香客問我拯刁,道長脊岳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任垛玻,我火速辦了婚禮割捅,結果婚禮上,老公的妹妹穿的比我還像新娘帚桩。我一直安慰自己亿驾,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布账嚎。 她就那樣靜靜地躺著莫瞬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醉锄。 梳的紋絲不亂的頭發(fā)上乏悄,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音恳不,去河邊找鬼。 笑死开呐,一個胖子當著我的面吹牛烟勋,可吹牛的內容都是我干的。 我是一名探鬼主播筐付,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼卵惦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瓦戚?” 一聲冷哼從身側響起沮尿,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畜疾,有當地人在樹林里發(fā)現了一具尸體赴邻,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年啡捶,在試婚紗的時候發(fā)現自己被綠了姥敛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞎暑,死狀恐怖彤敛,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情了赌,我是刑警寧澤墨榄,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站勿她,受9級特大地震影響渠概,放射性物質發(fā)生泄漏。R本人自食惡果不足惜嫂拴,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一播揪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筒狠,春花似錦、人聲如沸辩恼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灶伊。三九已至疆前,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間聘萨,已是汗流浹背竹椒。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留米辐,地道東北人胸完。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像翘贮,于是被迫代替她去往敵國和親赊窥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容