Vue 自定义过滤器、指令

210 阅读5分钟

vue-cli如何新增自定义指令?

// 创建局部指令
var app = new Vue({
    el: '#app',
    data: {    
    },
    // 创建指令(可以多个)
    directives: {
        // 指令名称
        dir1: {
            inserted(el) {
                // 指令中第一个参数是当前使用指令的DOM
                console.log(el);
                console.log(arguments);
                // 对DOM进行操作
                el.style.width = '200px';
                el.style.height = '200px';
                el.style.background = '#000';
            }
        }
    }
})

// 全局指令

Vue.directive('dir2', {
    inserted(el) {
        console.log(el);
    }
})

// 指令的使用
<div id="app">
    <div v-dir1></div>
    <div v-dir2></div>
</div>

vue如何自定义一个过滤器?

<div id="app">
     <input type="text" v-model="msg" />
     {{msg| capitalize }}
</div>
// 局部过滤器

var vm=new Vue({
    el:"#app",
    data:{
        msg:''
    },
    filters: {
      capitalize: function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
      }
    }
})
// 全局定义过滤器

Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

vue中自定义指令(阻止按钮重复提交)

  • 需求:
    • 按钮点击防抖,例:刷新当前组件并重新请求
    • 搜索框输入搜索,待输入停止后开始搜索
  • 思路:
    • 监听指定事件,事件被触发后清除之前事件
    • 需要监听的事件可能会改变,需要提取为变量
    • 时间也需要提取为变量
    • 防抖后执行的事件也是变量
    • 传递参数可选的有数组/对象
  1. 新建一个文件,directive.js
 // 阻止按钮重复提交
Vue.directive("button", {
    // 每当指令绑定到元素上的时候,会立即执行这个bind函数,只执行一次
    bind: function (el, binding, vnode) {
   
        function clickHandler(e) {
            // 这里判断点击的元素是否是本身,是本身,则返回
            if (el.contains(e.target)) {
                $(el).attr("disabled", true);
                setTimeout(() => {
                    $(el).attr("disabled", false);
                }, 1500)
                return false;
            }
            // 判断指令中是否绑定了函数
            // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
            if (binding.expression) {
                binding.value(e);
            }
        }
        // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
        el.__vueClickOutside__ = clickHandler;
        document.addEventListener('click', clickHandler);
    },
    unbind(el, binding) {   // 解除事件监听
        document.removeEventListener('click', el.__vueClickOutside__);
        delete el.__vueClickOutside__;
    },
})

2)在main.js中引入

import './directive'
  1. 在页面中使用
<div>
<el-button v-button @click="submit">登录</el-button>
</div>

图片懒加载(v-lazyload)

图片懒加载有两种方式:

  • 绑定srcoll事件进行监听
  • 使用intersectionobserver判断图片是否到了可视区域 思路:
  • 图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的
  • 拿到所有的图片Dom,遍历每个图片判断当前图片是否到了可视区范围内
  • 如果到了就设置图片的src属性,否则显示默认图片
  1. 新建一个文件,lazyload.js 先判断浏览器是否支持IntersectionObserverAPI,如果支持就使用,否则使用scroll事件监听+节流的方法实现
const LazyLoad = {
   // install方法
   install(Vue,options) {
      const defaultSrc = options.default
      Vue.directive('lazy',{
        bind(el,binding) {
          LazyLoad.init(el,binding.value,defaultSrc)
        },
        insterted(el){
           if(IntersectionObserver){
             LazyLoad.observe(el)
           }else {
           LazyLoad.instenerScroll(el)
           }
        }
      })
      
   },
   // 初始化
   init(el,val,def){
      el.setAttribute('data-src',val)
      el.setAttribute('src',def)
   },
   // 利用IntersectionObserver监听el
   observe(el){
     var io = new IntersectionObserver((entries) =>{
       const realSrc = el.dataset.src
       if(entries[0].isIntersecting){
         if(realSrc){
            el.src = realSrc
            el.removeAttribute('data-src')
         }
       }
     })
     io.observe(el)
   },
   // 监听scroll事件
   listenerScroll(el){
      const handlere = LazyLoad.throttle(LazyLoad.load,300)
      LazyLoad.load(el)
      window.addEventListener('scroll',()=>{
        handler(el)
      })
   },
   // 加载真实图片
   load(el){
      const windowHeight = document.documentElement.clientHeight
      const elTop = el.getBoundingClientRect().top
      const elBtm = el.getBoundingClientRect().bottom
      const realSrc = el.dataset.src
      if(elTop - windowHeight < 0 && elBtm > 0 ){
        if(realSrc){
          el.src = realSrc
          el.removeAttribute('data-src')
        }
      }
   },
   // 节流
   throttle(fn,delay){
     let timer
     let prevTime
     return function (...args){
       const currTime = Date.now()
       const 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

使用:

<img v-LazyLoad = 'xxx.png'>

自定义权限指令

思路:

  • 自定义一个权限数组
  • 判断用户的权限是否在这个数组内,如果是则显示,否则移除DOM
function checkArray(key){
   let arr = ['1','2','3','4']
   let index = arr.indexOf(key)
   if(index > -1){
      return true   // 有权限
   } else {
     return false   // 无权限
   }
}

const permission = {
   inserted:function(el,binding){
      let permission = binding.value // 获取到v-permission的值
      if(permission){
         let hasPermission = checkArray(permission)
         if(!hasPermission){
           // 没有权限。移除DOM元素
           el.parentNode && el.parentNode.removeChild(el)
         }
      }
   }
}
export default permission

使用:给v-permission赋值判断即可

<div class="btns">
  <button v-permission="'1'">权限按钮1</button>  // 显示
  <button v-permission="'2'">权限按钮2</button>  // 不显示
</div>

页面添加背景水印(v-waterMarker)

思路:

  • 使用canvas特性生成base64格式的图片文件,设置其字体大小,颜色等
  • 将其设置为背景图片,从而实现页面或组件水印效果
function addWaterMarker(str,parentNode,font,textColor){
   // 水印文字,父元素,字体,文字颜色
   var can = document.createElement('canvas')
   parentNode.appendChild(can)
   can.width = 200
   can.height = 150
   can.style.display = 'none'
   var cans = can.getContext('2d')
   cans.rotate((-20 * Math.PI)/180)
   cans.font = font || '16px Microsoft JhengHei'
   cans.fillStyle = textColor || 'rgba(180,180,180,0.3)'
   cnas.textAlign = 'left'
   cans.textBaseline = 'Middle'
   cans.fillText(str,can.width / 10,can.height /2)
   parentNode.style.backgroundImage = 'url(' +can.toDataURL('image/png') + ')'
}

const waterMarker = {
   bind:function(el,binding){             addWaterMarker(binding.value.text,el,binding.value.font.binding.value.textColor)
   }
}
export default waterMarker

使用:

<template>
   <div v-waterMarker="{text:'xxx版权所有',textColor:'rgba(180,180,180,0.4)'}"></div>
</template>

指令提供的钩子函数

【bind】   只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

【inserted】   被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。

【update】   所在组件的 VNode 更新时调用,但是可能发生在其孩子的 VNode 更新之前。指令的值可能发生了改变也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。

【componentUpdated】   所在组件的 VNode 及其孩子的 VNode 全部更新时调用。

【unbind】   只调用一次, 指令与元素解绑时调用。

钩子函数的参数

【el】   指令所绑定的元素,可以用来直接操作 DOM。

【binding】   一个对象,包含以下属性:

name: 指令名,不包括 v- 前缀。
value: 指令的绑定值, 例如: v-my-directive="1 + 1", value 的值是 2oldValue: 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression: 绑定值的字符串形式。 例如 v-my-directive="1 + 1" , expression 的值是 "1 + 1"arg: 传给指令的参数。例如 v-my-directive:foo, arg 的值是 "foo"modifiers: 一个包含修饰符的对象。 例如: v-my-directive.foo.bar, 修饰符对象 modifiers 的值是 { foo: true, bar: true }。

【vnode】   Vue 编译生成的虚拟节点。
【oldVnode】   上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。