当前位置:   article > 正文

Vue3 组合式函数_vue中组合式函数

vue中组合式函数

一、概述

1.1 概念解答

官方给出的解释:“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。

  • 有状态の逻辑
    有状态の逻辑负责管理随着时间而变化的状态。在项目开发过程中,如:触摸手势、数据库的连接状态这些功能的实现。
  • 无状态の逻辑
    在接收一些参数后,立即返回所期望的值,这就是无状态の逻辑。如构建前端应用时,常常会把一些日期格式化的函数,抽取到一个独立的 time.js 文件中,以便于在不同的组件或脚本中复用。当然无状态的逻辑库也有很多,如:lodash、moment 等。
  • 无渲染组件 :它只包括了可重用的逻辑 (数据获取、分页等) 而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。
    大部分能用无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现,并且还不会带来额外组件嵌套的开销。所以如果只是纯逻辑的封装(不需要视图输出),建议使用组合式API实现

1.2 核心知识概述

组合式函数约定用驼峰命名法命名,并以“use”作为开头。
可以通过抽取组合式函数改善代码结构。
抽取组合式函数不仅是为了复用,也是为了代码组织。随着组件复杂度的增高,你可能会最终发现组件多得难以查询和理解。组合式 API 会给予你足够的灵活性,让你可以基于逻辑问题将组件代码拆分成更小的函数:

<script setup>
import { useFeatureA } from './featureA.js'
import { useFeatureB } from './featureB.js'
import { useFeatureC } from './featureC.js'

const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在某种程度上,你可以将这些提取出的组合式函数看作是可以相互通信的组件范围内的服务。

数字化管理平台
Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus教程
权限系统-商城
个人博客地址

1.3 与 Mixin 对比

mixin 是 Vue2 中的 api,它也让我们能够把组件逻辑提取到可复用的单元里。然而 mixins 有三个主要的短板:

  1. 不清晰的数据来源
    当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。
  2. 命名空间冲突
    多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。
  3. 隐式的跨 mixin 交流
    多个 mixin 需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。
    所以,官方在 Vue 3 中不再推荐继续使用 mixin。

三、灵活性

随着组件复杂度的增高,你可能会最终发现组件多得难以查询和理解。组合式 API 会给予你足够的灵活性,让你可以基于逻辑问题将组件代码拆分成更小的函数。这更利于代码的组织和复用,从而改善代码结构。

<script setup>
import { useFeatureA } from './featureA.js'
import { useFeatureB } from './featureB.js'
import { useFeatureC } from './featureC.js'

const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、案例

组件中定义鼠标跟随案例

<script setup>
import { onMounted, onUnmounted, reactive } from 'vue';

// 实现鼠标追踪器效果
const movePosi = reactive({ x: 0, y: 0 })
function MyMouseMove(e) {
    movePosi.x = e.clientX
    movePosi.y = e.clientY
}

onMounted(() => window.addEventListener('mousemove', MyMouseMove))
onUnmounted(() => window.removeEventListener('mousemove', MyMouseMove))
</script>
<template>
    <div class="mouseRender" :style="{ top: movePosi.y + 'px', left: movePosi.x + 'px' }">
        <div>x:{{ movePosi.x }}</div>
        <div>y:{{ movePosi.y }}</div>
    </div>
</template>
<style scoped>
.mouseRender {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1000;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

如果想在多个组件中复用这个逻辑,需要把这个逻辑以一个组合式函数的形式提取到外部文件中,如下:

// event.js
import { onMounted, onUnmounted } from 'vue'

export function useEventListener(target, event, callback) {
  // 如果你想的话,
  // 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素
  onMounted(() => target.addEventListener(event, callback))
  onUnmounted(() => target.removeEventListener(event, callback))
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

可以嵌套多个组合式函数:一个组合式函数可以调用一个或多个其他的组合式函数。这使得我们可以像使用多个组件组合成整个应用一样,用多个较小且逻辑独立的单元来组合形成复杂的逻辑。

import { ref,reactive } from 'vue'
import { useEventListener } from './event'

// 按照惯例,组合式函数以“use”开头
export function useMouse() {
    const movePosi = reactive({ x: 0, y: 0 })

  // 组合式函数可以随时更改其状态。
 useEventListener(window, 'mousemove', (event) => {
    movePosi.x = event.clientX
    movePosi.y = event.clientY
  })
  
   // 通过返回值暴露所管理的状态
   return movePosi 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

注:组合式函数约定用驼峰命名法命名,并以“use”作为开头。

<script setup>
import { useMouse } from './mouse.js'

const { x, y } = useMouse()
</script>

<div class="mouseRender" :style="{ top: y + 'px', left: x + 'px' }">
        <div>x:{{ x }}</div>
        <div>y:{{ y }}</div>
    </div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

五、异步状态

useMouse() 组合式函数可以接收一个参数。在做异步数据请求时,我们常常需要处理不同的状态:加载中、加载成功和加载失败。fetch请求返回Promise对象,想要获取数据需要做进一步处理。

  1. 封装 fetch 请求,在项目中可以通用
// fetch.js
import { ref } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  fetch(url)
    .then((res) => res.json())
    .then((json) => (data.value = json))
    .catch((err) => (error.value = err))

  return { data, error }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 在组件中引入并调用
<script setup>
import { useFetch } from './fetch.js'

const { data, error } = useFetch('...')
</script>
  • 1
  • 2
  • 3
  • 4
  • 5

六、接收响应式状态

useFetch() 接收一个静态 URL 字符串作为输入——因此它只会执行一次 fetch 并且就此结束。如果我们想要在 URL 改变时重新 fetch 呢?

const url = ref('/initial-url')
// 1. 接收一个 ref
const { data, error } = useFetch(url)
// 这将会重新触发 fetch
url.value = '/new-url'

// 2. 接收一个getter
// 当 props.id 改变时重新 fetch
const { data, error } = useFetch(() => `/posts/${props.id}`)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

为了实现这一点,我们需要将响应式状态传入组合式函数,并让它基于传入的状态来创建执行操作的侦听器。
用 watchEffect() 和 toValue() API 来重构 fetch.js

// fetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  watchEffect(() => {
    // 在 fetch 之前重置状态
    data.value = null
    error.value = null
    // toValue() 将可能的 ref 或 getter 解包
    fetch(toValue(url))
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  })

  return { data, error }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

toValue() 是一个在 3.3 版本中新增的 API。它的设计目的是将 ref 或 getter 规范化为值。如果参数是 ref,它会返回 ref 的值;如果参数是函数,它会调用函数并返回其返回值。否则,它会原样返回参数。它的工作方式类似于 unref(),但对函数有特殊处理。

注意 toValue(url) 是在 watchEffect 回调函数的内部调用的。这确保了在 toValue() 规范化期间访问的任何响应式依赖项都会被侦听器跟踪。

这个版本的 useFetch() 现在能接收静态 URL 字符串、ref 和 getter,使其更加灵活。watch effect 会立即运行,并且会跟踪 toValue(url) 期间访问的任何依赖项。如果没有跟踪到依赖项(例如 url 已经是字符串),则 effect 只会运行一次;否则,它将在跟踪到的任何依赖项更改时重新运行。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/185357
推荐阅读
相关标签
  

闽ICP备14008679号