vue自定义指令产生的背景

  • 咱们晓得前端程序员,写的各种代码,最终是要作用于页面上的DOM元素的。比方用HTML固定好DOM的根底构造,用CSS设置DOM的款式,用JS去做一些DOM的交互。因为DOM元素相当于“物理层”,要出现给用户看的。DOM相当于积木一样,咱们去操作这个积木,最终“搭建”成各式各样的成果进去。

    所以所有花里胡哨的前端框架都是,基于前端三件套HTML、CSS、JS封装进去的
  • 所以jQuery中就通过$符,间接操作DOM就挺不便的,不便是不便了,然而间接操作DOM会引起页面的回流重绘,又会略为有点节约浏览器的性能,容易呈现页面卡顿的状况,造成用户体验不太好。

    万事皆有利弊,间接操作DOM,有时候,也可能,会是一个简略方便快捷的抉择。
  • 所以vue和react就闪亮退场了,用数据驱动的思维,并不是间接操作DOM,通过虚构DOM做一个缓冲,虚构DOM会收集谁须要操作DOM,操作了DOM的哪些货色,对立先记录好,最终全副一块操作DOM。有点像js文档碎片的感觉。这样的话,浏览器性能就会优化晋升不少。所以,vue中并不提倡间接操作DOM,因为有虚构DOM在呢,你还间接操作实在DOM干啥啊。用vue指令啊

    虚构DOM的确是个好货色,给大佬点赞。咱们晓得vue提供的内置指令很不便,能解决大多数的DOM操作问题,然而,并不能解决所有的问题。
  • 所以某些状况下,vue内置指令不能满足咱们的需要,咋办呢。当然Vue创始人也想到了这个问题,所以,就搞了vue自定义指令,封装了一套钩子函数和对应参数的规定、以供咱们应用去,从而更不便解决相应问题

    vue自定义指令就是间接操作DOM了,某些场景下,vue自定义指令是一个很好的抉择

vue自定义指令的分类

vue自定义指令有两种

  • 1. 全局自定义指令(须要全局注册)
  • 2. 组件自定义指令(须要组件外部注册)

自定义指令其实就是一个对象,对象身上有一些钩子函数,自定义指令对象身上的钩子函数和vue组件的生命周期钩子函数相似,后续会通过案例论述

个别应用全局自定义指令会多一些,毕竟复用不便

vue自定义指令应用解说

案例一 el-input初始获取焦点(组件自定义指令 )

这个案例其实官网的案例,这里咱们批改一下,更加便于咱们了解之。

假如咱们须要这样一个成果:页面加载结束当前,el-input输入框主动获取焦点,相似关上百度一下页面当前,输入框主动获取焦点一样。

<template>  <div>    <!-- 将指令挂在元素上 -->    <el-input v-myFocus></el-input>  </div></template><script>export default {  name:"myCode"  // 本例是部分自定义指令,所以directives写在组件外部  directives: {    // 给指令起个名字,叫做myFocus    myFocus: {      inserted: function (el) { // inserted钩子是这个元素行将插入到文档里的时候,具体程序是在created后、mounted前。可看打印程序                        // el参数是:自定义写在谁身上,挂在那个DOM元素上,这个el就是那个dom元素对象,钩子函数还有别的参数,这个后续会说        console.log("自定义指令-->", el.childNodes);        // 先让元素聚焦 el-input是在原生input外包了一层,所以须要通过原生js取子元素,找到input,具体看一下打印后果就分明了        el.childNodes[1].focus(); // 执行原生input的focus办法      },    },  },  created() { console.log("created-->") },  mounted() { console.log("mounted-->") }}</script>
演示的话,间接复制粘贴代码即可

案例二 仿写v-show性能(全局自定义指令)

效果图如下

看了效果图,性能其实和v-show没啥区别,举这样一个例子是不便更加好了解vue自定义指令

第一步,新建utils文件夹中寄存index.js文件,此文件用于书写全局自定义指令

// 引入vue并应用vue的directive办法去注册一个自定义指令import Vue from 'vue'Vue.directive('showshow', { // 指令的名字叫做showshow    // bind函数个别用来做初始化数据,也能够绑定事件什么的    bind(el, binding, vnode) {        console.log(el, binding.value, vnode);        // el参数是以后应用指令的元素,bind参数是指令绑定的数据,vnode是虚构dom        const flag = binding.value // 找到组件中绑定的标识        if (flag == false) {            el.style.display = 'none'        } else {            el.style.display = 'inline-block'        }    },    // inserted函数是在元素插入dom节点调用    inserted(el, binding, vnode) { },    // update和componentUpdated都是更新应用,然而前者更加罕用些,oldVnode参数只有在这两个钩子中才会有    componentUpdated(el, binding, vnode, oldVnode) { },    update(el, binding, vnode, oldVnode) {        const flag = binding.value        if (flag == false) {            el.style.display = 'none'        } else {            el.style.display = 'inline-block'        }    },    // unbind解绑时候应用,比方用来移除第一个bind函数中绑定的事件    unbind(el, binding, vnode) { }});

第二步,在main.js中引入这个书写全局自定义指令的文件(示意应用之)

import Vue from 'vue'import App from './App.vue'import router from "@/router/index.js" //引入路由表import store from './store/index' // 引入vuex// ...// 引入就能够应用全局自定义指令啦 ^_^import './views/utils/index.js'let vvvue = new Vue({  render: h => h(App),  router,  store // 挂载下来}).$mount('#app')

第三步,在组件中应用全局自定义指令

<template>  <div>    <el-button @click="change" type="primary">显示暗藏来回切换</el-button>    <!--         给这个按钮加上vue的自定义指令v-showshow ,并且让这个自定义指令绑定vue组件中data的数据        所以这里绑定的是isShowBtn标识,通过管制这个标识管制显示与暗藏    -->    <el-button type="primary" plain v-showshow="isShowBtn">仿写v-show按钮</el-button>  </div></template><script>export default {  data() {    return {      isShowBtn: true, // 初始为true,让其显示    };  },  methods: {    // 点击来回切换显示与暗藏    change() { this.isShowBtn = !this.isShowBtn },  },};</script>
有的道友说,蛤?这不是多此一举嘛?其实不是,因为还没遇到特定的场景。上述两个例子次要是学习自定义指令的思维。接下来咱们举一个小例子,点击文字一键复制性能。

案例三 实现v-copy自定义指令

效果图

点击就复制胜利了,而后在电脑的相应地位就能够执行 Ctrl V 就能够间接粘贴这个点击的文本了

第一步,写自定义指令代码

import Vue from 'vue'Vue.directive('copy', { // 指令的名字叫做v-copy    // bind函数初始化    bind(el, binding, vnode) { },    // inserted函数是在元素插入dom节点调用    inserted(el, binding, vnode) {  // el参数是以后应用指令的元素,bind参数是指令绑定的数据,vnode是虚构dom        // 将copyFn函数挂在el身上方便使用        el.copyFn = () => {            console.log('点击的是哪个DOM', el);            //创立选中范畴            var range = document.createRange();            // 抉择点击的这个dom            range.selectNode(el);            //移除剪切板中内容,不增加这个语句的话,在ie和Edge中复制不到            window.getSelection().removeAllRanges();            //将el中的文字内容复制到剪切板            window.getSelection().addRange(range);            // 开启复制粘贴性能            let flag = document.execCommand('copy');            // 需注意兼容性问题            flag ? alert('复制胜利,能够粘贴啦') : alert('以后浏览器不反对一键复制性能,请手动复制粘贴')        }        el.addEventListener('click', el.copyFn)    },    // update和componentUpdated都是更新应用,然而前者更加罕用些    // oldVnode参数只有在这两个钩子中才会有,更新当前才会有更新前和更新后的DOM    componentUpdated(el, binding, vnode, oldVnode) {        console.log('componentUpdated');    },    update(el, binding, vnode, oldVnode) {        console.log('update');    },    // unbind解绑时候应用,比方用来移除第一个bind函数中绑定的事件    unbind(el, binding, vnode) {        el.removeEventListener('click', el.copyFn)    }});/** * 为了疾速上手自定义指令钩子,咱们能够这样简略了解。 * * bind和inserted钩子  -->  相似created和mounted钩子 * componentUpdated和update钩子  -->  相似updated钩子 * unbind钩子相似  -->  destroyed钩子 * * 具体程序大家能够拷贝代码运行当前打印看一下,这样更加不便精准了解 * */

第二步,在.vue文件中应用

<template>  <div>    <!-- 给每一项加上v-copy自定义指令 -->    <div class="item" v-copy v-for="(item, index) in arr" :key="index">{{ item }}</div>  </div></template><script>export default {  name: "TestVue",  data() {    return {      arr: ["孙悟空", "猪八戒", "沙和尚", "唐僧"],    };  },};</script><style lang="less" scoped>.item {  margin-top: 12px;  font-size: 24px;  font-weight: bolder;  cursor: pointer;}</style>

补充案例中常识

  • Range对象:https://developer.mozilla.org...
  • Selection对象:https://developer.mozilla.org...
  • execCommand办法:https://developer.mozilla.org...
对于MDN说,execCommand办法未来可能被删除的这个问题,思否上有一个答复挺好的,也顺带贴上:https://segmentfault.com/q/10...

总结

在什么时候须要用自定义指令?

当内置指令不够用的时候,当须要做一些非凡的成果的时候,当须要对一般DOM元素进行底层操作的时候

自定义指令就是贴近原生js书写形式,咱们正当应用其钩子函数以及相应参数,能够实现更加灵便的成果性能。毕竟vue尽管提供了很多的内置指令,不便咱们间接应用,上手快,然而也是限度了程序员的一些发散思维。各有利弊,所以须要衡量 ^_^