vue自定义指令使用~以仿写v-show和实现v-copy为例讲解

简介: vue自定义指令使用~以仿写v-show和实现v-copy为例讲解

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功能(全局自定义指令)

效果图如下

1.gif

看了效果图,功能其实和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自定义指令

效果图

121212.gif

点击就复制成功了,然后在电脑的相应位置就可以执行 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>

补充案例中知识

关于MDN说,execCommand方法将来可能被删除的这个问题,思否上有一个回答挺好的,也顺带贴上: https://segmentfault.com/q/1010000022841994?utm_source=tag-newest

总结

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

当内置指令不够用的时候,当需要做一些特殊的效果的时候,当需要对普通DOM元素进行底层操作的时候

自定义指令就是贴近原生js书写方式,我们合理使用其钩子函数以及相应参数,可以实现更加灵活的效果功能。毕竟vue虽然提供了很多的内置指令,方便我们直接使用,上手快,但是也是限制了程序员的一些发散思维。各有利弊,所以需要权衡 ^_^

相关文章
|
3天前
|
JavaScript
Vue中如何设置在执行删除等危险操作时给用户提示(二次确认后执行对应的操作)
这篇文章介绍了在Vue项目中如何实现执行删除等危险操作时的二次确认机制,使用Element UI的`el-popconfirm`组件来弹出确认框,确保用户在二次确认后才会执行删除操作。
Vue中如何设置在执行删除等危险操作时给用户提示(二次确认后执行对应的操作)
|
3天前
|
JavaScript
如何创建一个Vue项目(手把手教你)
这篇文章是一篇手把手教读者如何创建Vue项目的教程,包括使用管理员身份打开命令行窗口、找到存放项目的位置、通过vue-cli初始化项目、填写项目信息、进入项目目录、启动项目等步骤,并提供了一些常见第三方库的引入方法。
如何创建一个Vue项目(手把手教你)
|
3天前
|
前端开发
StringBoot+Vue实现游客或用户未登录系统前、可以浏览商品等信息、但是不能购买商品或者加入购物车等操作。登录系统显示用户的登录名(源码+讲解)
这篇文章介绍了使用StringBoot+Vue实现用户登录状态判断的方法,包括前端加载用户信息和后端设置session的源码示例。
|
3天前
|
JavaScript
如何在Vue页面中引入img下的图片作为背景图。../的使用
这篇文章介绍了在Vue页面中如何引入`img`目录下的图片作为背景图,提供了两种使用相对路径的方法。第一种是使用`../assets/img/`作为路径引入图片,第二种是使用`../../assets/img/`作为路径。文章还展示了使用这些方法的代码实现和效果展示,并鼓励读者学无止境。
如何在Vue页面中引入img下的图片作为背景图。../的使用
|
3天前
|
JavaScript
如何通过点击商品的信息(图片或者文字)跳转到更加详细的商品信息介绍(前后端分离之Vue实现)
该博客文章介绍了如何在Vue 2框架下实现前后端分离的商品信息展示和详情页跳转,包括排序筛选、详情展示、加入购物车和分享功能。
如何通过点击商品的信息(图片或者文字)跳转到更加详细的商品信息介绍(前后端分离之Vue实现)
|
3天前
|
JavaScript
如何查看Vue使用的版本
这篇文章介绍了如何在项目中查看Vue及其相关库的版本信息,比如element-ui和element-china-area-data。要查看Vue的版本,需要查看项目中的`package.json`文件,在`dependencies`部分可以找到Vue的版本号。如果需要查询不同版本的兼容性,可以访问相应的官方文档或资源网站。
|
3天前
|
存储 JavaScript 前端开发
Vue中如何通过三元运算符来展示不同的操作
这篇文章讲述了在Vue中如何使用三元运算符结合v-if指令来根据订单的不同状态展示不同的操作按钮,例如在待发货状态显示退款按钮,在待付款或完成状态显示删除按钮。
|
JavaScript 前端开发
【vuejs深入一】深入学习vue指令,自定义指令解决开发痛点
写在前面    一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残。   最近博主我沉淀了几个月,或者说懒了几个月。然而大佬的指点总是一针见血,能够让人看到方向。所以我现在有觉得,一个好的学习环境指的一定是有个能指点你的大佬。
2360 0
|
3天前
|
JavaScript 编译器
成功解决:Module build failed: Error: Vue packages version mismatch
这篇文章记录了解决Vue项目中遇到的"Module build failed: Error: Vue packages version mismatch"错误的步骤,原因是项目中Vue依赖的版本不一致,解决方法是删除`node_modules`后重新安装指定版本的Vue和`vue-template-compiler`,确保版本匹配,最终成功运行项目。
成功解决:Module build failed: Error: Vue packages version mismatch
|
3天前
|
JavaScript
在Vue中使用Avue、配置过程以及实际应用
这篇文章介绍了作者在Vue项目中集成Avue组件库的完整过程,包括安装、配置和实际应用,展示了如何利用Avue实现动态表单和数据展示的功能。
在Vue中使用Avue、配置过程以及实际应用