>  기사  >  웹 프론트엔드  >  Vue3의 결합된 기능 프로그래밍 방법은 무엇입니까

Vue3의 결합된 기능 프로그래밍 방법은 무엇입니까

王林
王林앞으로
2023-05-11 08:49:121637검색

컴포저블 함수란 무엇인가요?

Vue 애플리케이션의 개념에서 "컴포저블"은 Vue의 컴포저블 API를 사용하여 상태 저장 로직을 캡슐화하고 재사용하는 함수입니다.

프런트 엔드 애플리케이션을 구축할 때 일반적인 작업의 논리를 재사용해야 하는 경우가 많습니다. 예를 들어, 다른 장소에서 시간 형식을 지정하기 위해 재사용 가능한 날짜 형식 지정 기능을 추출할 수 있습니다. 이 함수는 상태 비저장 논리를 캡슐화합니다. 즉, 일부 입력을 받아 원하는 출력을 즉시 반환합니다. lodash 또는 date-fns와 같이 상태 비저장 논리를 재사용하는 라이브러리가 많이 있습니다.

반대로 상태 저장 논리는 시간이 지남에 따라 변경되는 상태를 관리하는 역할을 담당합니다. 간단한 예는 페이지에서 현재 마우스 위치를 추적하는 것입니다. 실제 애플리케이션에서는 터치 제스처나 데이터베이스 연결 상태와 같은 더 복잡한 논리일 수도 있습니다.

마우스 추적기 예시

구성 요소에서 직접 구성 API를 사용하여 마우스 추적 기능을 구현한다면 다음과 같을 것입니다.

<script setup>
import { ref, onMounted, onUnmounted } from &#39;vue&#39;
const x = ref(0)
const y = ref(0)
function update(event) {
  x.value = event.pageX
  y.value = event.pageY
}
onMounted(() => window.addEventListener(&#39;mousemove&#39;, update))
onUnmounted(() => window.removeEventListener(&#39;mousemove&#39;, update))
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>

하지만 이 동일한 논리를 여러 구성 요소에서 재사용하려면 어떻게 해야 할까요? 이 로직을 결합된 함수의 형태로 외부 파일로 추출할 수 있습니다.

// mouse.js
import { ref, onMounted, onUnmounted } from &#39;vue&#39;
// 按照惯例,组合式函数名以“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(&#39;mousemove&#39;, update))
  onUnmounted(() => window.removeEventListener(&#39;mousemove&#39;, update))
  // 通过返回值暴露所管理的状态
  return { x, y }
}

컴포넌트에서 사용되는 방법은 다음과 같습니다.

<script setup>
import { useMouse } from &#39;./mouse.js&#39;
const { x, y } = useMouse()
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>

Vue3의 결합된 기능 프로그래밍 방법은 무엇입니까

보시다시피 핵심 로직은 우리가 사용한 로직과 정확히 동일합니다. 그냥 외부 함수로 옮기고 노출해야 하는 상태를 반환하면 됩니다. 구성 요소와 마찬가지로 구성 함수에서도 모든 구성 API를 사용할 수 있습니다. 이제 useMouse() 기능을 모든 구성 요소에서 쉽게 재사용할 수 있습니다. useMouse()的功能可以在任何组件中轻易复用了。

更酷的是,你还可以嵌套多个组合式函数:一个组合式函数可以调用一个或多个其他的组合式函数。这使得我们可以像使用多个组件组合成整个应用一样,用多个较小且逻辑独立的单元来组合形成复杂的逻辑。实际上,这正是为什么我们决定将实现了这一设计模式的 API 集合命名为组合式 API。

举例来说,我们可以将添加和清除 DOM 事件监听器的逻辑也封装进一个组合式函数中:

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

有了它,之前的useMouse()组合式函数可以被简化为:

// mouse.js
import { ref } from &#39;vue&#39;
import { useEventListener } from &#39;./event&#39;
export function useMouse() {
  const x = ref(0)
  const y = ref(0)
  useEventListener(window, &#39;mousemove&#39;, (event) => {
    x.value = event.pageX
    y.value = event.pageY
  })
  return { x, y }
}

TIP

每一个调用useMouse()的组件实例会创建其独有的xy状态拷贝,因此他们不会互相影响。

异步状态示例

useMouse()组合式函数没有接收任何参数,因此让我们再来看一个需要接收一个参数的组合式函数示例。在做异步数据请求时,我们常常需要处理不同的状态:加载中、加载成功和加载失败。

<script setup>
import { ref } from &#39;vue&#39;
const data = ref(null)
const error = ref(null)
fetch(&#39;...&#39;)
  .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 class="brush:php;toolbar:false">{{ data }}
     
Loading...

如果在每个需要获取数据的组件中都要重复这种模式,那就太繁琐了。让我们把它抽取成一个组合式函数:

// fetch.js
import { ref } from &#39;vue&#39;
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 &#39;./fetch.js&#39;
const { data, error } = useFetch(&#39;...&#39;)
</script>

useFetch()接收一个静态的 URL 字符串作为输入,所以它只执行一次请求,然后就完成了。但如果我们想让它在每次 URL 变化时都重新请求呢?那我们可以让它同时允许接收 ref 作为参数:

// fetch.js
import { ref, isRef, unref, watchEffect } from &#39;vue&#39;
export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  function doFetch() {
    // 在请求之前重设状态...
    data.value = null
    error.value = null
    // unref() 解包可能为 ref 的值
    fetch(unref(url))
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  }
  if (isRef(url)) {
    // 若输入的 URL 是一个 ref,那么启动一个响应式的请求
    watchEffect(doFetch)
  } else {
    // 否则只请求一次
    // 避免监听器的额外开销
    doFetch()
  }
  return { data, error }
}

这个版本的useFetch()现在同时可以接收静态的 URL 字符串和 URL 字符串的 ref。当通过isRef()检测到 URL 是一个动态 ref 时,它会使用watchEffect()启动一个响应式的 effect。该 effect 会立刻执行一次,并在此过程中将 URL 的 ref 作为依赖进行跟踪。当 URL 的 ref 发生改变时,数据就会被重置,并重新请求。

约定和最佳实践

命名

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

输入参数

尽管其响应性不依赖 ref,组合式函数仍可接收 ref 参数。如果编写的组合式函数会被其他开发者使用,你最好在处理输入参数时兼容 ref 而不只是原始的值。unref()工具函数会对此非常有帮助:

import { unref } from &#39;vue&#39;
function useFeature(maybeRef) {
  // 若 maybeRef 确实是一个 ref,它的 .value 会被返回
  // 否则,maybeRef 会被原样返回
  const value = unref(maybeRef)
}

如果你的组合式函数在接收 ref 为参数时会产生响应式 effect,请确保使用watch()显式地监听此 ref,或者在watchEffect()中调用unref()来进行正确的追踪。

返回值

你可能已经注意到了,我们一直在组合式函数中使用ref()而不是reactive()

더 멋진 점은 여러 개의 결합된 함수를 중첩할 수도 있다는 것입니다. 결합된 함수는 하나 이상의 다른 결합된 함수를 호출할 수 있습니다. 이를 통해 여러 구성 요소가 결합되어 전체 애플리케이션을 형성하는 것처럼 여러 개의 더 작고 논리적으로 독립적인 장치를 결합하여 복잡한 논리를 형성할 수 있습니다. 실제로 이것이 바로 우리가 이 디자인 패턴을 구현하는 API 컬렉션을 Composable API라고 부르기로 결정한 이유입니다.

예를 들어 DOM 이벤트 리스너를 추가하고 삭제하는 논리를 결합된 함수로 캡슐화할 수 있습니다.

// x 和 y 是两个 ref
const { x, y } = useMouse()

이를 사용하면 이전 useMouse() 결합 함수를 다음과 같이 단순화할 수 있습니다. 🎜
const mouse = reactive(useMouse())
// mouse.x 链接到了原来的 x ref
console.log(mouse.x)
🎜TIP 🎜🎜useMouse()를 호출하는 각 구성 요소 인스턴스는 고유한 x, y 상태 복사본을 생성하므로 서로 영향을 미치지 않습니다. 🎜🎜비동기 상태 예시🎜🎜useMouse()결합 함수는 매개변수를 전혀 받지 못하므로 하나의 매개변수를 받아야 하는 결합 함수의 또 다른 예를 살펴보겠습니다. 비동기 데이터 요청을 할 때 로딩, 로딩 성공, 로딩 실패 등 다양한 상태를 처리해야 하는 경우가 많습니다. 🎜
Mouse position is at: {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->{ mouse.x }}, {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->{ mouse.y }}
🎜데이터를 가져와야 하는 모든 구성 요소에서 이 패턴을 반복하는 것은 너무 번거로울 것입니다. 이것을 구성된 함수로 추출해 보겠습니다. 🎜
<script setup>
import { useFeatureA } from &#39;./featureA.js&#39;
import { useFeatureB } from &#39;./featureB.js&#39;
import { useFeatureC } from &#39;./featureC.js&#39;
const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>
🎜이제 구성 요소에 필요한 것은 다음과 같습니다. 🎜
import { useMouse } from &#39;./mouse.js&#39;
import { useFetch } from &#39;./fetch.js&#39;
export default {
  setup() {
    const { x, y } = useMouse()
    const { data, error } = useFetch(&#39;...&#39;)
    return { x, y, data, error }
  },
  mounted() {
    // setup() 暴露的属性可以在通过 `this` 访问到
    console.log(this.x)
  }
  // ...其他选项
}
🎜useFetch()는 정적 URL 문자열을 입력으로 허용하므로 요청이 한 번만 실행됩니다. 완료. 하지만 URL이 변경될 때마다 다시 요청하려면 어떻게 해야 할까요? 그런 다음 ref를 매개변수로 동시에 수신하도록 허용할 수 있습니다. 🎜rrreee🎜이 버전의 useFetch()는 이제 정적 URL 문자열과 URL 문자열 참조를 모두 수신할 수 있습니다. isRef()를 통해 URL이 동적 참조임을 감지하면 watchEffect()를 사용하여 반응 효과를 시작합니다. 효과는 즉시 실행되며, 그 과정에서 URL의 참조가 종속성으로 추적됩니다. URL의 참조가 변경되면 데이터가 재설정되고 요청이 다시 이루어집니다. 🎜🎜규칙 및 모범 사례🎜

이름 지정

🎜결합된 기능에 대한 규칙은 낙타 표기 이름 지정을 사용하고 "사용"으로 시작하는 것입니다. 🎜

입력 매개변수

🎜 응답성이 참조에 의존하지 않더라도 구성된 함수는 여전히 참조 매개변수를 받을 수 있습니다. 다른 개발자가 사용할 구성된 함수를 작성하는 경우 원시 값보다는 입력 매개변수를 참조 호환성으로 처리하는 것이 좋습니다. unref() 유틸리티 함수는 이에 매우 유용할 수 있습니다. 🎜rrreee🎜구성된 함수가 ref를 매개변수로 받을 때 반응 효과를 생성하는 경우 watch()를 사용하여 명시적으로 시청하세요. ref를 사용하거나 watchEffect()에서 unref()를 호출하여 올바른 추적을 수행하세요. 🎜

반환 값

🎜결합된 함수에서 reactive() 대신 ref()를 사용했다는 것을 눈치채셨을 것입니다. 권장되는 규칙은 구성된 함수가 항상 여러 참조를 포함하는 일반 비반응형 객체를 반환하므로 객체가 구성 요소의 참조로 구조 해제된 후에도 응답성을 유지한다는 것입니다. 🎜🎜js🎜rrreee🎜구성된 함수에서 반응형 객체를 반환하면 객체 구조 분해 중에 손실되는 구성된 함수 내의 상태에 대한 반응적 연결입니다. 대조적으로, ref는 이러한 반응형 연결을 유지합니다. 🎜

如果你更希望以对象属性的形式来使用组合式函数中返回的状态,你可以将返回的对象用reactive()包装一次,这样其中的 ref 会被自动解包,例如:

const mouse = reactive(useMouse())
// mouse.x 链接到了原来的 x ref
console.log(mouse.x)
Mouse position is at: {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->{ mouse.x }}, {<!--{C}%3C!%2D%2D%20%2D%2D%3E-->{ mouse.y }}

副作用

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

  • 如果你的应用用到了服务端渲染(SSR),请确保在组件挂载后才调用的生命周期钩子中执行 DOM 相关的副作用,例如:onMounted()。这些钩子仅会在浏览器中被调用,因此可以确保能访问到 DOM。

  • 确保在onUnmounted()时清理副作用。举例来说,如果一个组合式函数设置了一个事件监听器,它就应该在onUnmounted()中被移除 (就像我们在useMouse()示例中看到的一样)。当然也可以像之前的useEventListener()示例那样,使用一个组合式函数来自动帮你做这些事。

使用限制

组合式函数在<script setup></script>setup()钩子中,应始终被同步地调用。在某些场景下,你也可以在像onMounted()这样的生命周期钩子中使用他们。

这个限制是为了让 Vue 能够确定当前正在被执行的到底是哪个组件实例,只有能确认当前组件实例,才能够:

  • 将生命周期钩子注册到该组件实例上

  • 将计算属性和监听器注册到该组件实例上,以便在该组件被卸载时停止监听,避免内存泄漏。

TIP

<script setup></script>是唯一在调用await之后仍可调用组合式函数的地方。编译器会在异步操作之后自动为你恢复当前的组件实例。

通过抽取组合式函数改善代码结构

抽取组合式函数不仅是为了复用,也是为了代码组织。随着组件复杂度的增高,你可能会最终发现组件多得难以查询和理解。组合式 API 会给予你足够的灵活性,让你可以基于逻辑问题将组件代码拆分成更小的函数:

<script setup>
import { useFeatureA } from &#39;./featureA.js&#39;
import { useFeatureB } from &#39;./featureB.js&#39;
import { useFeatureC } from &#39;./featureC.js&#39;
const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>

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

选项式API中使用组合式函数

如果你正在使用选项式 API,组合式函数必须在setup()中调用。且其返回的绑定必须在setup()中返回,以便暴露给this及其模板:

import { useMouse } from &#39;./mouse.js&#39;
import { useFetch } from &#39;./fetch.js&#39;
export default {
  setup() {
    const { x, y } = useMouse()
    const { data, error } = useFetch(&#39;...&#39;)
    return { x, y, data, error }
  },
  mounted() {
    // setup() 暴露的属性可以在通过 `this` 访问到
    console.log(this.x)
  }
  // ...其他选项
}

与其他模式的比较

和Mixin的对比

Vue 2 的用户可能会对mixins选项比较熟悉。它也让我们能够把组件逻辑提取到可复用的单元里。然而 mixins 有三个主要的短板:

  • 不清晰的数据来源:当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。

  • 命名空间冲突:多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。

  • 隐式的跨 mixin 交流:多个 mixin 需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。

基于上述理由,我们不再推荐在 Vue 3 中继续使用 mixin。保留该功能只是为了项目迁移的需求和照顾熟悉它的用户。

和无渲染组件的对比

在组件插槽一章中,我们讨论过了基于作用域插槽的无渲染组件。我们甚至用它实现了一样的鼠标追踪器示例。

组合式函数相对于无渲染组件的主要优势是:组合式函数不会产生额外的组件实例开销。当在整个应用中使用时,由无渲染组件产生的额外组件实例会带来无法忽视的性能开销。

我们推荐在纯逻辑复用时使用组合式函数,在需要同时复用逻辑和视图布局时使用无渲染组件。

和React Hooks的对比

如果你有 React 的开发经验,你可能注意到组合式函数和自定义 React hooks 非常相似。组合式 API 的一部分灵感正来自于 React hooks,Vue 的组合式函数也的确在逻辑组合能力上与 React hooks 相近。然而,Vue 的组合式函数是基于 Vue 细粒度的响应性系统,这和 React hooks 的执行模型有本质上的不同。

위 내용은 Vue3의 결합된 기능 프로그래밍 방법은 무엇입니까의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제