美文网首页
Vue-组合式函数

Vue-组合式函数

作者: 搬码人 | 来源:发表于2023-09-25 10:37 被阅读0次

什么是“组合式函数”?

就像父子组件之间组合一样,Vue的组合式API允许函数也能如组件一样组合。

例子

以下都是来自官方的两个示例

鼠标追踪器示例

这是一个追踪鼠标光标在窗口的实时位置demo:

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

const x = ref(0)
const y = ref(0)

function update(event) {
  x.value = event.pageX
  y.value = event.pageY
}

onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>

<template>Mouse position is at: {{ x }}, {{ y }}</template>

如果用组合式函数的方式对其改造:

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

// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
  // 被组合式函数封装和管理的状态
  const x = ref(0)
  const y = ref(0)

  // 组合式函数可以随时更改其状态。
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // 一个组合式函数也可以挂靠在所属组件的生命周期上
  // 来启动和卸载副作用
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 通过返回值暴露所管理的状态
  return { x, y }
}

然后是在组件中对其使用:

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

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

<template>Mouse position is at: {{ x }}, {{ y }}</template>

但其实,函数之间也能拆分再组合,对上方的代码再改造:

// 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))
}

这样,之前的useMouse()函数就可以这样写:

// mouse.js
import { ref } from 'vue'
import { useEventListener } from './event'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  useEventListener(window, 'mousemove', (event) => {
    x.value = event.pageX
    y.value = event.pageY
  })

  return { x, y }
}

异步状态示例

同样的异步请求方法可能被调用很多次,如果在使用到的组件中都重复写多次就会导致耦合性很高,所以不如把同样的异步请求部分抽离出来。
在做异步数据请求时,我们常常需要处理不同的状态:加载中、加载成功和加载失败。

<script setup>
import { ref } from 'vue'

const data = ref(null)
const error = ref(null)

fetch('...')
  .then((res) => res.json())
  .then((json) => (data.value = json))
  .catch((err) => (error.value = err))
</script>

<template>
  <div v-if="error">Oops! Error encountered: {{ error.message }}</div>
  <div v-else-if="data">
    Data loaded:
    <pre>{{ data }}</pre>
  </div>
  <div v-else>Loading...</div>
</template>

将上方异步请求部分进行抽离:

// 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 }
}

这样,在组件中使用时:

<script setup>
import { useFetch } from './fetch.js'

const { data, error } = useFetch('...')
</script>

接收响应式状态

上方的异步请求组合函数还能继续改造,比如我们向传入响应式数据让其具有响应性,每次改变时都能再发起请求。
举例来说,useFetch() 应该能够接收一个 ref:

const url = ref('/initial-url')

const { data, error } = useFetch(url)

// 这将会重新触发 fetch
url.value = '/new-url'

或者接收一个 getter 函数:

// 当 props.id 改变时重新 fetch
const { data, error } = useFetch(() => `/posts/${props.id}`)

可以使用watchEffect()toValue()API来重构刚才的实现:

toValue() 是一个在 3.3 版本中新增的 API:将传入的ref、getter函数或普通数值都规范转换为普通数值。

// 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 }
}

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

约定和最佳实践

命名

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

输入参数

即便不依赖于 ref 或 getter 的响应性,组合式函数也可以接收它们作为参数。如果你正在编写一个可能被其他开发者使用的组合式函数,最好处理一下输入参数是 ref 或 getter 而非原始值的情况。可以利用 toValue() 工具函数来实现:

import { toValue } from 'vue'

function useFeature(maybeRefOrGetter) {
  // 如果 maybeRefOrGetter 是一个 ref 或 getter,
  // 将返回它的规范化值。
  // 否则原样返回。
  const value = toValue(maybeRefOrGetter)
}

如果你的组合式函数在输入参数是 ref 或 getter 的情况下创建了响应式 effect,为了让它能够被正确追踪,请确保要么使用 watch()显式地监视 ref 或 getter,要么在 watchEffect() 中调用toValue()

返回值

1、 返回值使用ref()而不是reactive(),因为reactive()存在解构丢失响应性。
2、 如果你更希望以对象属性的形式来使用组合式函数中返回的状态,你可以将返回的对象用 reactive() 包装一次,这样其中的 ref 会被自动解包,例如:

const mouse = reactive(useMouse())
// mouse.x 链接到了原来的 x ref
console.log(mouse.x)
Mouse position is at: {{ mouse.x }}, {{ mouse.y }}

副作用

在组合式函数中的确可以执行副作用 (例如:添加 DOM 事件监听器或者请求数据),但请注意以下规则:

  • 如果你的应用用到了服务端渲染 (SSR),请确保在组件挂载后才调用的生命周期钩子中执行 DOM 相关的副作用,例如:onMounted()。这些钩子仅会在浏览器中被调用,因此可以确保能访问到 DOM。
  • 确保在onUnmounted() 时清理副作用。举例来说,如果一个组合式函数设置了一个事件监听器,它就应该在 onUnmounted()中被移除 (就像我们在useMouse()示例中看到的一样)。当然也可以像之前的 useEventListener()示例那样,使用一个组合式函数来自动帮你做这些事。

相关文章

  • JS继承方式总结 (转)

    借用构造函数继承 原型链式继承(借用原型链实现继承) 组合式继承 组合式继承优化1 组合式继承优化2 ES6中继承...

  • 2018-07-11

    vue-声明周期钩子函数 1、created 实例被创建后调用 var data = { a: 50}; var ...

  • JavaScript几种继承方式及其优缺点总结

    •借用构造函数 (又叫伪造对象或经典继承)•组合继承(也叫伪经典继承)•寄生组合式继承 ☞借用构造函数继承 原理:...

  • Facebook Componentkit 概况了解

    一、概况1.1Components 三大特性:声明式 Declarative:函数式Functional:组合式C...

  • 寄生组合式继承

    组合式继承: 组合继承的缺点:会调用两次父类型构造函数,在子类型的原型上创造了不必要的、多余的属性 寄生组合式继承...

  • js的继承方式

    js的继承方式 一、原型链继承 原型继承的缺点: 二. 构造函数继承 构造函数继承的缺点: 三. 组合式继承 组合...

  • 继承

    1.原型链 2.借用构造函数 3.组合继承 4.寄生组合式继承

  • js继承方式

    类式继承 构造函数继承 组合继承 类式继承 + 构造函数继承 原型式继承 寄生式继承 寄生组合式继承 寄生式继承 ...

  • Vue3.0的常用API

    组合式API 1、setup 使用 setup 函数时,它将接受两个参数:props、context。1、prop...

  • Vue组合式API

    Composition API简介 Composition API:组合式 API;一组低侵入式的、函数式的 AP...

网友评论

      本文标题:Vue-组合式函数

      本文链接:https://www.haomeiwen.com/subject/oanqbdtx.html