npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wdy_custom_hooks

v1.4.0

Published

one custom hooks of vue3

Downloads

124

Readme

基于 Vue3 的常用自定义指令钩子

v-focus:自动焦点

<template>
  <input v-focus />
</template>

v-resize:监听元素大小的变化

<template>
  <!-- 基本用法,默认200ms触发一次 -->
  <div v-resize="resizeCallback"></div>
  <!-- 支持自定义防抖时间 -->
  <div v-resize:300="resizeCallback"></div>
</template>

<script setup lang="ts">
// 元素大小的变化回调函数
const resizeCallback = (e) => {
  console.log("回调触发了:" + e);
};
</script>

v-copy:复制文本

<template>
  <button v-copy="copyConfig">点击复制</button>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { ElMessage } from "element-plus";

const copyConfig = ref({
  text: "Hello,World!", // 要复制的文本
  // 自定义复制成功的回调,可选,默认console.log("复制成功")
  success: () => {
    ElMessage.success("复制成功");
  },
  // 自定义复制失败的回调,可选,默认console.error("复制失败")
  error: (err: any) => {
    ElMessage.error("失败:" + err);
  },
});
</script>

v-scrollLoad:滚动加载更多

<template>
  <div style="height: 100vh; overflow: auto" v-scrollLoad="scrollConfig">
    <ul>
      <li
        v-for="item in items"
        :key="item"
        style="padding: 30px; font-size: 20px"
      >
        {{ item }}
      </li>
    </ul>
    <div
      v-if="loading"
      style="
        height: 50px;
        display: flex;
        justify-content: center;
        font-size: 20px;
      "
    >
      <el-icon><Loading /></el-icon>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { Loading } from "@element-plus/icons-vue";

const loading = ref(false);
const items = ref<string[]>([
  "Item 1",
  "Item 2",
  "Item 3",
  "Item 4",
  "Item 5",
  "Item 6",
  "Item 7",
  "Item 8",
  "Item 9",
  "Item 10",
]);

// 指令配置项
const scrollConfig = ref({
  distance: 100, // 距离底部的距离,可选,默认举例底部100px触发回调
  loadMoreFn: loadMore, // 回调函数
});

// 模拟请求,加载更多数据
async function loadMore() {
  if (loading.value) return;
  if (items.value.length >= 30) return; // 假设数据只有30条
  loading.value = true;
  console.log("触发了回调");
  // 模拟加载更多数据, 每次加载10条数据;
  for (let i = 0; i < 10; i++) {
    items.value.push("Item " + (items.value.length + 1));
  }
  loading.value = false;
}
</script>

v-imgLazy:图片懒加载

<template>
  <div>
    <img v-lazyLoad="imgConfig" alt="Image" />
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";

const imgConfig = ref({
  // 真实图片地址
  src: "https://q4.itc.cn/images01/20241017/4cf3fe55a9584eb0b2c111f9e0baabfd.jpeg",
  // 占位图地址,可选,没有默认的占位图
  placeholderSrc:
    "https://img.zcool.cn/community/01efbc5a1e6efda80120908dcb1567.gif",
  // 加载失败图地址,可选,没有默认的加载失败图
  errorSrc:
    "https://gss0.baidu.com/7Po3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/574e9258d109b3de93ad439cc9bf6c81800a4c37.jpg",
});
</script>

v-longPress:绑定长按事件

<template>
  <div>
    {{ count }}
    <el-button type="primary" v-longPress="longPressConfig"> 长按我 </el-button>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";

const count = ref(1);

const longPressConfig = ref({
  callback: () => {
    console.log("长按事件触发!");
    count.value++;
  }, // 长按事件触发的回调函数
  time: 2000, // 长按事件触发的时间,可选,默认 2000ms
});
</script>

v-waterMarker:页面水印

<template>
  <div v-waterMarker="waterMarkerConfig" style="width: 100%; height: 100vh;">
    <h1>我的标题</h1>
    <p>这是页面内容……</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";

const waterMarkerConfig = ref({
  text: "某某档案@[email protected]", // 水印内容
  font: "16px Microsoft Yahei", // 字体样式,可选,默认 '16px Microsoft Yahei'
  color: "rgba(0, 0, 0, 0.1)", // 字体颜色,可选,默认 'rgba(0, 0, 0, 0.1)'
  rotate: -20, // 字体旋转角度,可选, 默认 -20
  labelGap: 50, // 水印之间的间距,可选, 默认 50
});
</script>

v-outsideClick:点击元素外部事件

<template>
  <div v-outsideClick="clickOutside" style="position: relative; margin: 30px">
    <button @click="visible = true">下拉菜单</button>
    <div
      v-if="visible"
      style="
        position: absolute;
        top: 30px;
        z-index: 99;
        background-color: #bfa;
        width: 150px;
        height: 200px;
        padding: 6px 10px;
        border: 1px solid #d9d9d9;
      "
    >
      <ul>
        <li>菜单项1</li>
        <li>菜单项2</li>
        <li>菜单项3</li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const visible = ref(false);

const clickOutside = () => {
  visible.value = false;
};
</script>

v-debounce:防抖指令

<template>
  <el-button type="primary" v-debounce:click="debounceConfig"
    >防抖测试{{ count }}</el-button
  >
</template>

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);

const debounceConfig = ref({
  callback: () => {
    // 点击事件触发的回调函数
    console.log("点击了按钮");
    count.value++;
  },
  delay: 500, // 点击事件触发的时间,可选,默认 300ms
});
</script>

v-throttle:节流指令

<template>
  <el-button type="primary" v-debounce:click="debounceConfig"
    >节流测试{{ count }}</el-button
  >
</template>

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);

const throttleConfig = ref({
  callback: () => {
    // 点击事件触发的回调函数
    console.log("点击了按钮");
    count.value++;
  },
  delay: 500, // 点击事件触发的时间,可选,默认 300ms
});
</script>

v-permission:权限按钮指令

<template>
  <div>
    <button v-permission:sys:user:add="permissions">添加用户</button>
    <button v-permission:[dynaimcAuth]="permissions">删除角色</button>
  </div>
</template>
<script setup>
import { ref, onMounted } from "vue";

// 模拟用户权限列表,实际项目中可以从 Vuex 或 Pinia或请求 中获取
const permissions = ref(["sys:user:add", "sys:role:add", "sys:role:delete"]);

// 动态按钮权限
const dynaimcAuth = ref("sys:user:delete");

onMounted(() => {
  setTimeout(() => {
    dynaimcAuth.value = "sys:role:delete"; // 模拟按钮权限变化
    permissions.value = ["sys:role:add", "sys:role:delete"]; // 模拟用户权限列表变化
  }, 3000);
});