VueUse 中的常用hooks

788 阅读4分钟

官方文档: vueuse.org/

中文文档: www.vueusejs.com/

一、介绍

Vueuse 是一个基于 Vue3 的功能强大的 Vue.js 生态系统工具库,它提供了一系列的可重用的 Vue组件Composition API(组合式API)函数,旨在帮助开发者更高效地构建 Vue 应用。这个库利用 Vue 3 的响应式系统,提供了一系列可复用的功能函数(也称为“钩子”或“hooks”),这些函数覆盖了从基础的状态管理到集成浏览器API的高级用途。

Vueuse 具有以下主要特点,使其成为 Vue.js 开发中的有力助手:

  • 丰富的钩子函数:VueUse 提供了大量的钩子函数,覆盖了诸如访问浏览器位置、使用本地存储、监听事件、实现响应式状态等常见需求。

  • 基于 Composition API:所有的钩子函数都是基于 Vue 3 的 Composition API 设计的,这意味着它们可以在 Vue 组件的 setup 函数中直接使用,使得代码更加模块化和可复用。

  • TypeScript 支持:Vueuse 对 TypeScript 提供了良好的支持,所有的组件和函数都有完整的类型定义,提供了更好的代码提示和类型安全性。

  • 可按需引入(轻量级和模块化) :Vueuse 由一系列独立的模块组成,每个模块都提供一个特定的功能或功能集,你可以只引入需要的钩子函数,而不必引入整个库,这有助于减小最终打包文件的大小。

二、使用

安装

下载包的方式:

npm i @vueuse/core

CDN 引入方式:

<script src="https://unpkg.com/@vueuse/shared"></script>
<script src="https://unpkg.com/@vueuse/core"></script>

使用方法

VueUse中的大多数函数都返回一个refs对象,你可以使用ES6的对象解构语法来获取。

<script setup>
  import { useLocalStorage, useMouse } from '@vueuse/core'
  
  // 跟踪鼠标位置
  const { x, y } = useMouse() 
  console.log(x.value)

  // 将状态保存在 localStorage 中
  const store = useLocalStorage(
    'my-storage',
    {
      name: 'Apple',
      color: 'red',
    },
  )
</script>

三、常用函数

templateRef(将 ref 绑定到模板元素)

<template>
  <div ref="target"></div>
</template>

<script lang="ts">
import { templateRef } from '@vueuse/core'

export default {
  setup() {
    const target = templateRef('target')
    // 无需返回 target,它会自动绑定到模板元素上
  }
}
</script>

<script setup> 中,所有变量都将暴露在模板中,你可以使用 ref 代替 templateRef,因为它们的功能是相同的。

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

const target = ref<HTMLElement | null>(null)
</script>

useVModel(v-mode)

v-model 简写,props + emit -> ref

父组件:

<MyCheckbox v-model:checkedVal="isChecked"></MyCheckbox>

<script setup>
  const isChecked = ref(true)
</script>

MyCheckbox.vue:

<script setup>
  import { useVModel } from '@vueuse/core'

  const props = defineProps({
    checkedVal: {
      type: Boolean,
      default: false
    }
  })
  
  const emit = defineEmits(['update:checkedVal'])

  const checked = useVModel(props, 'checkedVal', emit)

  const changeChecked = () => {
    checked.value = !checked.value
  }
</script>

useToggle(切换布尔值状态)

<template>
  <div v-loading="loading"></div>
</template>

<script setup>
  import { useToggle } from '@vueuse/core';

  const [loading, toggleLoading] = useToggle(false)

  const loadList = async () => {
    try {
      toggleLoading(true)
      // xxx
    } catch (error) {
      // xxx
    } finally {
      toggleLoading(false)
    }
  }
</script>

useAsyncState(获取异步状态)

响应式获取异步状态。不会阻塞 setup 函数,在 promise 完成后,将自动触发。

<template>
  <div v-loading="isLoading">
    <div @click="onRefresh">Refresh</div>
  </div>
</template>

<script setup>
  import axios from 'axios'
  import { useAsyncState } from '@vueuse/core'

  const { state, isReady, isLoading, execute: loadList } = useAsyncState(
    axios
    .get('https://jsonplaceholder.typicode.com/todos/1')
    .then(t => t.data),
    { id: null },
  )

  async function onRefresh() {
    // xxx
    await loadList()
  }
</script>

useMouse(获取鼠标当前位置)

<template>
  <div>
    <p>X: {{ mouse.x }}, Y: {{ mouse.y }}</p>
  </div>
</template>
 
<script setup>
import { useMouse } from '@vueuse/core';

const mouse = useMouse();
</script>

useWindowSize(获取窗口尺寸)

<template>
  <div>
    <div>{{ width }} - {{ height }}</div>
  </div>
</template>
<script setup>
  import { useWindowSize } from "@vueuse/core";
  
  const { width, height } = useWindowSize();
</script>

useLocalStorage(本地存储)

<script setup>
  import { useLocalStorage } from '@vueuse/core';

  // 获取
  const name = useLocalStorage('name', ''); // 第一个参数是键名,第二个参数是默认值
  console.log(name.value)
  
  // 设置
  useLocalStorage('name', '').value = 'a';

  // 清除
  useLocalStorage('name', '').value = null;
  // 或者使用 remove 方法  name.remove();
</script>

useClipboard(剪切板功能)

<template>
  <div>
    <input v-model="input" type="text" />
    <button @click="copy(input)">Copy</button>
  </div>
</template>
<script setup>
  import { useClipboard } from "@vueuse/core";
  
  const input = ref("");
  // text: 复制的内容; copy: 复制函数; isSupported: 当前的浏览器是否支持这个api
  const { text, copy, isSupported } = useClipboard();
</script>

useFullscreen(全屏)

<template>
 <button @click="toggle">全屏</button>
</template>
<script setup>
  import { useFullscreen } from "@vueuse/core";
  
  const { isFullscreen, toggle } = useFullscreen();
</script>

useEventListener(监听事件)

useEventListener监听事件后,组件卸载时自动调用removeEventListener,不用担心注册多个事件而导致内存泄露的问题。

<template>
  <div>
    <div ref="element">useEventListener</div>
  </div>
</template>
<script setup>
  import { useEventListener } from "@vueuse/core";
  
  const element = ref(null);
  
  useEventListener(element, "click", () => {
    console.log("监听点击事件");
  });
</script>

useTransition(值之间的过渡)

我们可以使用 useTransition 来过渡整个数字数组,这在处理位置或颜色时很有用。

处理颜色的一个绝招是使用一个计算属性将RGB值格式化为正确的颜色语法。

<template>
  <h2 :style="{ color: color } "> COLOR CHANGING </h2>
</template>

<script setup>
import { useTransition, TransitionPresets } from '@vueuse/core'
  
const source = ref([0, 0, 0])
  
const output = useTransition(source, {
  duration: 3000,
  transition: TransitionPresets.easeOutExpo,
})
  
const color = computed(() => {
  const [r, g, b] = output.value
  return `rgb(${r}, ${g}, ${b})`
})
  
source.value = [255, 0, 255]
</script>

useDebounceFn(防抖)

<template>
  <div>
    <div>{{ counter }}</div>
    <button @click="addNum">add</button>
  </div>
</template>
<script lang="ts" setup>
  import { useDebounceFn } from "@vueuse/core";
  const counter = ref(0);
  const handleAddFun = () => {
    counter.value += 1;
  };
  const addNum = useDebounceFn(handleAddFun, 300, { maxWait: 5000 });
</script>