ホームページ > ウェブフロントエンド > Vue.js > Vue3の関数を組み合わせたプログラミング手法とは何ですか

Vue3の関数を組み合わせたプログラミング手法とは何ですか

王林
リリース: 2023-05-11 08:49:12
転載
1702 人が閲覧しました

結合関数とは

Vue アプリケーションの概念における「結合可能な関数」(Composables) は、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の関数を組み合わせたプログラミング手法とは何ですか

#Asご覧のとおり、コア ロジックはまったく同じで、これを外部関数に移動し、公開する必要がある状態を返すだけです。コンポーネントと同様に、すべての合成 API を合成関数で使用できます。

useMouse() の機能は、どのコンポーネントでも簡単に再利用できるようになりました。

さらに優れているのは、複数の結合関数をネストできることです。結合関数は 1 つ以上の他の結合関数を呼び出すことができます。これにより、複数のコンポーネントを組み合わせてアプリケーション全体を形成するのと同じように、複数のより小さく論理的に独立したユニットを組み合わせて複雑なロジックを形成することができます。実際、まさにこれが、このデザイン パターンを実装する 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() を呼び出す各コンポーネント インスタンスは、独自の一意の x, y# を作成します。 ## ステータスはコピーされるため、相互に影響を与えることはありません。 非同期状態の例

useMouse()

結合関数はパラメーターを受け取らないため、1 つのパラメーターを受け取る必要がある結合関数の別の例を見てみましょう。非同期データリクエストを行うときは、ロード、ロード成功、ロード失敗などのさまざまな状態を処理する必要があることがよくあります。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">&lt;script setup&gt; import { ref } from &amp;#39;vue&amp;#39; const data = ref(null) const error = ref(null) fetch(&amp;#39;...&amp;#39;) .then((res) =&gt; res.json()) .then((json) =&gt; (data.value = json)) .catch((err) =&gt; (error.value = err)) &lt;/script&gt; &lt;template&gt; &lt;div v-if=&quot;error&quot;&gt;Oops! Error encountered: {{ error.message }}&lt;/div&gt; &lt;div v-else-if=&quot;data&quot;&gt; Data loaded: &lt;pre class=&quot;brush:php;toolbar:false&quot;&gt;{{ data }}</pre><div class="contentsignin">ログイン後にコピー</div></div> </div> <div v-else>Loading...</div> </template></pre>データを取得する必要があるすべてのコンポーネントでこのパターンを繰り返すのは面倒すぎます。これを結合関数に抽出しましょう:

// 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 文字列を入力として受け取ります。リクエストを 1 回実行すると完了します。しかし、URL が変更されるたびに再リクエストしたい場合はどうすればよいでしょうか?次に、パラメータとして ref を同時に受信できるようにすることができます。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">// fetch.js import { ref, isRef, unref, watchEffect } from &amp;#39;vue&amp;#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) =&gt; res.json()) .then((json) =&gt; (data.value = json)) .catch((err) =&gt; (error.value = err)) } if (isRef(url)) { // 若输入的 URL 是一个 ref,那么启动一个响应式的请求 watchEffect(doFetch) } else { // 否则只请求一次 // 避免监听器的额外开销 doFetch() } return { data, error } }</pre><div class="contentsignin">ログイン後にコピー</div></div>このバージョンの

useFetch()

は、静的 URL 文字列と URL 文字列 ref の両方を受信できるようになりました。 isRef() を介して URL が動的参照であることを検出すると、watchEffect() を使用してリアクティブ効果を開始します。エフェクトはすぐに実行され、その過程で URL の参照が依存関係として追跡されます。 URL の参照が変更されると、データはリセットされ、リクエストが再度実行されます。 規約とベスト プラクティス

名前付け

結合関数の規約では、キャメル ケースの名前付けを使用し、「use」で始めることです。

入力パラメータ

その応答性は ref に依存しませんが、合成された関数は引き続き ref パラメータを受け取ることができます。他の開発者が使用する合成関数を作成している場合は、単なる生の値ではなく、ref 互換性を備えた入力パラメーターを処理することをお勧めします。この場合、unref() ユーティリティ関数が非常に役立ちます。

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

組み合わせた関数が ref をパラメータとして受け取るときにリアクティブ効果を生成する場合は、必ず

watch()

を使用してください。スタイルでこの ref をリッスンすることを表示するか、正しい追跡のために watchEffect()unref() を呼び出します。 戻り値

結合関数で

reactive()

の代わりに ref() を使用していることに気づいたかもしれません。私たちが推奨する規則は、合成関数が常に複数の ref を含むプレーンな非リアクティブ オブジェクトを返すことです。これにより、オブジェクトはコンポーネント内の ref に構造化された後も応答性を維持します。 js

// x 和 y 是两个 ref
const { x, y } = useMouse()
ログイン後にコピー

Returning合成関数からのリアクティブ オブジェクトにより、オブジェクトの構造化中に合成関数内の状態へのリアクティブ接続が失われます。対照的に、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>setup()钩子中,应始终被同步地调用。在某些场景下,你也可以在像onMounted()这样的生命周期钩子中使用他们。

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

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

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

TIP

<script setup>是唯一在调用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 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート