31.自定义指令

2021-09-02  本文已影响0人  静昕妈妈芦培培

认识自定义指令

在Vue的模板语法中我们学习过各种各样的指令:v-show、v-for、v-model等等,除了使用这些指令之外,Vue
也允许我们来自定义自己的指令

自定义指令分为两种:

比如我们来做一个非常简单的案例:当某个元素挂载完成后可以自定获取焦点

实现方式一:如果我们使用默认的实现方式;
<template>
  <div>
    <input type="text" ref="inputRef" />
  </div>
</template>

<script>
import {ref, onMounted} from "vue"
export default {
  setup() {
    const inputRef = ref(null)
    onMounted(() => {
      inputRef.value.focus()
    })
    return {inputRef};
  },
};
</script>

<style scoped></style>

实现方式二:自定义一个 v-focus 的局部指令;
<template>
  <div>
    <input type="text" v-focus />
  </div>
</template>

<script>
export default {
  directives: {
    focus: {
      mounted(el, bindings, vnode, preVNode) {
        el.focus();
      },
    },
  },
  setup() {
    return {};
  },
};
</script>

<style scoped></style>

实现方式三:自定义一个 v-focus 的全局指令;

main.js

import { createApp } from "vue";
import App from "./4_directives/1.input自动获取焦点.vue";
const app = createApp(App);
app.directive("focus", {
   mounted(el, bindings, vnode, preVNode) {
     el.focus();
   },
 });
app.mount("#app");

App.vue

<template>
  <div>
    <input type="text" v-focus />
  </div>
</template>

<script>
export default {
  setup() {
    return {};
  },
};
</script>

<style scoped></style>

指令的生命周期

一个指令定义的对象,Vue提供了如下的几个钩子函数:

<template>
  <div>
    <input type="text" v-focus />
  </div>
</template>

<script>
export default {
  directives: {
    focus: {
      created(el, bindings, vnode, preVNode) {
        //此时元素的属性和事件还没绑定
        console.log("created", "此时元素的属性和事件还没绑定");
      },
      beforeMount(el, bindings, vnode, preVNode) {
        console.log("beforeMount", "此时绑定元素的父组件还没挂载");
      },
      mounted(el, bindings, vnode, preVNode) {
        //此时绑定元素的父组件已挂载
        console.log("mounted", "此时绑定元素的父组件已挂载");
        el.focus();
      },
      beforeUpdate(el, bindings, vnode, preVNode) {
        console.log("beforeUpdate", "在更新包含的组件的vnode之前调用");
      },
      undated(el, bindings, vnode, preVNode) {
        console.log("updated", "在包含组件的 VNode 及其子组件的 VNode 更新后调用;");
      },
      beforeUnmount(el, bindings, vnode, preVNode) {
        console.log("beforeUnmount", "在卸载绑定元素的父组件之前调用");
      },
      unmounted(el, bindings, vnode, preVNode) {
        console.log("unmounted", "当指令与元素解除绑定且父组件已卸载时,只调用一次;");
      }
    },
  },
  setup() {
    return {};
  },
};
</script>

<style scoped></style>

指令的参数和修饰符

如果我们指令需要接受一些参数或者修饰符应该如何操作呢?

<template>
  <div>
    <button v-why:info.aaa.bbb="{ name: 'coderwhy', age: '18' }">
      {{ count }}
    </button>
  </div>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
  directives: {
    why: {
      mounted(el, bindings, vnode, preVNode) {
        //此时绑定元素的父组件已挂载
        console.log("mounted", "此时绑定元素的父组件已挂载");
        console.log(bindings);
      },
    },
  },
  setup() {
    const count = ref(100);
    return { count };
  },
};
</script>

<style scoped></style>


在我们的生命周期中,我们可以通过 bindings 获取到对应的内容:
image.png

例:自定义时间格式化指令

./directives/time-format.js

import dayjs from 'dayjs'

export default function (app) {
  app.directive('format-time', {
    //在绑定的元素的父组件被挂载后调用
    mounted(el,bindings,vnode,preVnode) {
      //获取绑定元素的内容
      const textContent = el.textContent
      let timestamp = parseInt(textContent)

      //假如时间戳为10位说明单位为s,为13为,说明单位为毫秒
      if(textContent.length === 10) {
        timestamp = timestamp * 1000
      }
      //获取v-format-time的值
      let formatString = bindings.value
      //假如v-format-time的值不存在,设置默认值
      if(!formatString) {
        formatString = 'YYYY-MM-DD  HH:mm:ss'
      }


      el.textContent =  dayjs(timestamp).format(formatString)
    }
  })

}

./directives/index.js

import timeFormat from './time-format'

export default function registerDirectives(app) {
  timeFormat(app)
}

在main.js中注册

import { createApp } from "vue";
import App from "./4_directives/3.指令的参数和修饰符.vue";
import registerDirectives from "./directives/index";
const app = createApp(App);

app.use(registerDirectives);
app.mount("#app");

使用

<template>
  <div>
    <!-- 此处需要用单引号引起来,否则会认为YYYY-MM-DD是个变量 -->
    <div v-format-time="'YYYY-MM-DD'">1624777616</div>
  </div>
</template>

<script>
  export default {
    
  }
</script>

例:input值格式化
./directives/nuber-input.js

function onInput(el, elInput, bindings, vnode) {
  return function handle() {
    // 只保留数字
    //获取input输入框的值
    let val = elInput.value;
    // modifiers为修饰符对象,传入了float,则其float属性为true
    //假如使用了.float修饰符,输入框可以输入带小数点的浮点数
    if (bindings.modifiers.float) {
      // 清除"数字"和"."以外的字符
      val = val.replace(/[^\d.]/g, "");
      // 只保留第一个, 清除多余的  ..   ...  ....等都清除  .保留
      val = val.replace(/\.{2,}/g, ".");
      // 第一个字符如果是.号,则补充前缀0
      val = val.replace(/^\./g, "0.");

      //假如指令有传值,如:v-number-input.float="2"
      if (typeof bindings.value !== "undefined") {
        // 期望保留的最大小数位数
        let pointKeep = 0;
        if (
          typeof bindings.value === "string" ||
          typeof bindings.value === "number"
        ) {
          pointKeep = parseInt(bindings.value);
        }
        if (!isNaN(pointKeep)) {
          //如果pointkeep是有效数字
          if (!Number.isInteger(pointKeep) || pointKeep < 0) {
            //如果pointkeep不是整数或者是负数 设置pointkeep为0
            pointKeep = 0;
          }
          const str = "^(\\d+)\\.(\\d{" + pointKeep + "}).*$";
          const reg = new RegExp(str);
          if (pointKeep === 0) {
            // 不需要小数点
            val = val.replace(reg, "$1");
          } else {
            // 通过正则保留小数点后指定的位数
            val = val.replace(reg, "$1.$2");
          }
        }
      }
      if (Number(val) > 10000000) val = 10000000;
      elInput.value = val + "";
    } else {
      //假如没有使用.float修饰符,输入框只能输入整数  v-number-input 输入框所有非数字替换为空字符串
      elInput.value = elInput.value.replace(/[\D]/g, "");
    }
  };
}

export default function(app) {
  app.directive("number-input", {
    mounted(el, bindings, vnode, preVnode) {
      console.log(vnode);
      //获取input元素:假如指令绑定的元素不是input元素,获取其包含的input元素
      const elInput = el.tagName === "INPUT" ? el : el.querySelector("input");

      //给input元素添加事件
      elInput.addEventListener(
        "keyup",
        onInput(el, elInput, bindings, vnode),
        false
      );
    },
  });
}

<input v-number-input />
<input v-number-input.float='2' />

此文档主要内容来源于王红元老师的vue3+ts视频教程

上一篇下一篇

猜你喜欢

热点阅读