【vue2】自定义指令,以(复制v-copy / 预览图片v-imagePreview)为例

2 篇文章 0 订阅
1 篇文章 0 订阅
在vue 中,官方给我们提供了v-model、v-html 等这些方便好使的指令。这使得我们能在日常开发中大大提高了我们的开发效率。
那么除了官方提供的指令以外,我们同样也可以自定义封装指令。其实指令可以理解为我们经常封装的 js 工具类就行,都是为了方便好使、好管理。只不过是在自定义指令的时候,我们要按照vue官方提供好的方式去分装就行。
下面我们就以日常中比较常用到的【复制文本】和【预览图片】功能为例,记录下我们是如何创建自己的指令的:

1. 创建指令目录文件

个人比较喜欢将项目中的指令,统一创建到一个文件夹下,如在vue2 项目中的 src 目录下创建一个 directive 文件夹用来存放所有的指令代码文件,目录如下图所示:
在这里插入图片描述

创建 index.js文件,用来管理所有的指令文件
url: src/directive/index.js
代码如下所示:

/**
 * 指令的钩子函数:
 * vue2指令文档:https://vuejs.bootcss.com/guide/custom-directive.html
 *
 * bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
 * inserted: 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
 * update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
 * componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
 * unbind: 只调用一次,指令与元素解绑时调用。
 */

import copy from "./v-copy";
import imagePreview from "./v-imagePreview";

// 自定义指令
const directives = {
  copy,
  imagePreview,
};

export default {
  // 全局注册多文件指令
  install(Vue) {
    Object.keys(directives).forEach((key) => {
      Vue.directive(key, directives[key]);
    });
  },
};

2. 在main.js 中全局引入指令

这边我用到的是全局引入,因为都是些常用的指令。当然,如果你希望是局部引入,那就按需引入就行。

import Directive from "@/directive/index.js"; // 自定义指令
Vue.use(Directive);

3. 开始创建指令

作者这边用到了 vant 的UI组件库,你们可根据自己的项目要求自行替换即可。

vant 的使用具体参照其官方文档 https://vant-contrib.gitee.io/vant/#/zh-CN/quickstart

复制文本指令【v-copy
url: src/directive/v-copy.js
代码如下:

/**
 * 复制指令:v-copy
 * 用法:<button v-copy="`这是你要复制的文本`">copyText</button>
 */

// vant 的提示api(可替换成你们的)
// import { Toast } from "vant";

const copy = {
  /**
   * 指令绑定再DOM时调用,只执行一次
   * @param {*} el 指令所绑定的元素,可以用来直接操作 DOM
   * @param {*} binding 指令的信息,里面的value为指令绑定的值
   */
  bind(el, binding) {
    el.$value = binding.value;

    // 创建事件-复制文本逻辑
    el.copyFun = () => {
      if (!el.$value) {
        // Toast("暂无复制内容");
        console.log("暂无复制内容")
        return;
      }

      const textarea = document.createElement("textarea"); // 手动创建 textarea 文本标签
      textarea.value = el.$value; // 设置要复制的值

      document.body.appendChild(textarea); // 将 textarea 标签添加到页面中
      textarea.select(); // 选中文本框的值

      try {
        const result = document.execCommand("Copy"); // 复制
        if (result) {
          // Toast("复制成功"); // 复制成功
          console.log("复制成功")
        } else {
          // Toast("复制失败"); // 复制失败
          console.log("复制失败")
        }
      } catch {
        // 浏览器不支持
        // Toast("复制失败,请检查浏览器是否兼容");
        console.log("复制失败,请检查浏览器是否兼容")
      }

      document.body.removeChild(textarea); // 移除 textarea 标签
    };

    el.addEventListener("click", el.copyFun); // 绑定点击事件
  },

  /* 组件数据更新之后值行 */
  componentUpdated(el, binding) {
    el.$value = binding.value;
  },

  /* 与元素解绑时执行 */
  unbind(el) {
    // console.log("coyp-unbind:", el);
    el.removeEventListener("click", el.copyFun);
  },
};

export default copy;

预览图片指令【v-imagePreview
url: src/directive/v-imagePreview.js
代码如下:

/**
 * 预览图片 v-imagePreview
 * 使用:
 *
 * 方式一:v-imagePreview:[index]="imageList"
 * 参数:
 * index:需要预览图片的下标
 * imageList:图片数组 如:["https://img.jpg","https://img2.jpg"],
 *
 * 方式二:(适合动态配置 ImagePreview 的其他属性)
 * v-imagePreview="{ images: imageList, startPosition: 1(图片索引) }"
 * 参数:
 * 传入vant ImagePreview Api 的相关配置属性
 *
 */

// 这里使用的是 vant ui组件里的 预览图片api, 可以按你们的项目要求替换
import { ImagePreview } from "vant";

const imagePreview = {
  bind(el, binding) {
    el.imgList = binding.value; // 图片的值
    el.imgIndex = binding.arg || null; // 图片下标

    // 创建事件
    el.ImagePreview = () => {
      // 两种传值方式都可以(具体可参看顶部的使用说明)
      if (el.imgIndex) {
        // 方式一:(可替换成你们项目的预览图片的方式)
        ImagePreview({
          images: el.imgList,
          startPosition: el.imgIndex,
        });
      } else {
        // 方式二:(可替换成你们项目的预览图片的方式)
        ImagePreview(el.imgList);
      }
    };

    el.addEventListener("click", el.ImagePreview); // 绑定点击事件
  },

  componentUpdated(el, binding) {
    // 赋值
    el.imgList = binding.value;
    el.imgIndex = binding.arg || null; // 图片下标
  },

  unbind(el) {
    el.removeEventListener("click", el.copyFun);
  },
};

export default imagePreview;

4. 使用自定义指令

全局引入的话就直接使用即可

<template>
  <div>
    <!-- v-copy -->
    <h2>v-copy的使用</h2>
    <van-button v-copy="`这是你要复制的文本`"
                type="primary">v-copy复制文本</van-button>

    <!-- v-imagePreview -->
    <h2>v-imagePreview的使用</h2>
    <div class="img-box">
      <!-- 预览图片-方式一 -->
      <img v-for="(item, index) in imageList"
           :key="index"
           :src="item"
           v-imagePreview:[index]="imageList"
           alt="" />
      
      <!-- 预览图片-方式二 -->
      <!-- <img v-for="(item, index) in imageList"
           :key="index"
           :src="item"
           v-imagePreview="{ images: imageList, startPosition: index }"
           alt="" /> -->
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      imageList: [
        'https://img01.yzcdn.cn/vant/apple-1.jpg',
        'https://img01.yzcdn.cn/vant/apple-2.jpg'
      ]
    }
  }
}
</script>

<style lang="scss" scoped>
.img-box {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 30px;
  img {
    width: 100px;
    height: 100px;
    margin: 0 20px;
  }
}
</style>

以上是vue2的自定义指令的方式。


至于vue3的话估计封装自定义指令的方式和钩子函数会稍有不同,具体可以参考下vue3的官方文档。https://www.javascriptc.com/vue3js/guide/custom-directive.html#%E7%AE%80%E4%BB%8B

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值