Skip to content

开源库

vueuse 是一个开源的 实用的 vue 组合式 API 库

倒计时

js
import { isCallable } from "@/tools";
import { ref } from "vue";

// 倒计时
export function useCountDown(initSecs = 5) {
  const seconds = initSecs;
  const countdown = ref(seconds);

  let timer;
  function startCountdown(onEnd) {
    timer && clearTimeout(timer);
    timer = setInterval(() => {
      countdown.value -= 1;
      if (countdown.value <= 0) {
        timer && clearTimeout(timer);
        isCallable(onEnd) && onEnd();
      }
    }, 1000);
  }

  function restartCountdown(onEnd) {
    countdown.value = seconds;
    startCountdown(onEnd);
  }

  return {
    countdown,
    startCountdown,
    restartCountdown,
  };
}
vue
<template>
  <button @click="handleClick">
    <span>发送短信验证码</span>
    <span v-if="isDisabled">({{ countdown }})</span>
  </button>
</template>

<script setup>
import { ref } from "vue";
import { useCountDown } from "@/hooks";

const isDisabled = ref(false);
const { countdown, restartCountdown } = useCountDown(60);

function handleClick() {
  if (isDisabled.value) {
    return;
  }

  isDisabled.value = true;
  restartCountdown(() => {
    isDisabled.value = false;
  });
}
</script>

可重置的响应式对象

js
import { reactive, ref } from "vue";

// 深度克隆
function deepClone(obj) {
  return structuredClone(obj);
}

// 可以重置的 reactive
export function useResettableRective(obj) {
  const initial = deepClone(obj);
  const state = reactive(obj);

  function reset() {
    return Object.assign(state, initial);
  }

  // 可以这样返回, 看编码习惯
  // return { state, reset };
  // return [ state, reset ];

  // 也可以直接这样
  state.$reset = reset;
  return state;
}

// 可以重置的ref
export function useResettableRef(value) {
  const initial = deepClone(value);
  const state = ref(value);

  function reset() {
    state.value = deepClone(initial);
  }

  state.$reset = reset;
  return state;
}
vue
<template>
  <form action="">
    <input type="text" v-model="formData1.email" placeholder="email" />
    <input type="password" v-model="formData1.password" placeholder="password" />
  </form>
  <form action="">
    <input type="text" v-model="formData2.email" placeholder="email" />
    <input type="password" v-model="formData2.password" placeholder="password" />
  </form>
  <button @click="handleClick">reset</button>
</template>

<script setup>
import { useResettableRef, useResettableReactive } from "./hooks";

const formData1 = useResettableReactive({
  email: "",
  password: "",
});

const formData2 = useResettableRef({
  email: "",
  password: "",
});

function handleClick() {
  formData1.$reset();
  formData2.$reset();
}
</script>

页面关闭之前弹出提示

ts
import { isRef, onMounted, onUnmounted, type ComputedRef, type Ref } from "vue";

interface BeforeUnloadOptions {
  message: string;
  enabled: boolean | Ref<boolean> | ComputedRef<boolean>;
}

export function useBeforeUnload(opts: Partial<BeforeUnloadOptions>) {
  const defaultOptions: BeforeUnloadOptions = {
    message: "Are you sure you want to leave this page?",
    enabled: true,
  };
  const options = Object.assign(defaultOptions, opts);

  function beforeUnloadHandler(event: BeforeUnloadEvent) {
    const { message, enabled } = options;
    if ((typeof enabled === "boolean" && enabled) || (isRef(enabled) && enabled.value)) {
      event.preventDefault();
      event.returnValue = message;
      return message;
    }
  }

  onMounted(() => {
    window.addEventListener("beforeunload", beforeUnloadHandler);
  });

  onUnmounted(() => {
    window.removeEventListener("beforeunload", beforeUnloadHandler);
  });
}
vue
<template>
  <button @click="mockRequest">发送请求 {{ stateText }}</button>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import { useBeforeUnload } from "./hooks/useBeforeUnload";

const isLoading = ref(false);
const stateText = computed(() => {
  return isLoading.value ? "(加载中...)" : "";
});

useBeforeUnload({
  message: "正在请求中, 确定要离开当前页面吗?",
  enabled: isLoading, // 如果在请求时刷新页面就弹出提示,否则就不弹出
});

function mockRequest() {
  if (isLoading.value) {
    return;
  }
  isLoading.value = true;
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(true);
      isLoading.value = false;
    }, 5 * 1000);
  });
}
</script>

Released under the MIT License.