vue3?邏輯復用的實現(xiàn)示例
什么是組合式函數(shù)
_無狀態(tài)邏輯的函數(shù)_:它在接收一些輸入后立刻返回所期望的輸出。
比如 時間格式化的函數(shù)。
有狀態(tài)邏輯的函數(shù): 有狀態(tài)邏輯負責管理會隨時間而變化的狀態(tài)。
比如 跟蹤當前鼠標在頁面中的位置。
在 Vue 應用的概念中,“組合式函數(shù)”(Composables) 是一個利用 Vue 的組合式 API 來封裝和復用有狀態(tài)邏輯的函數(shù)。
一個簡單的例子是跟蹤當前鼠標在頁面中的位置。在實際應用中,也可能是像觸摸手勢或與數(shù)據(jù)庫的連接狀態(tài)這樣的更復雜的邏輯。
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from "vue";
const x = ref(0);
const y = ref(0);
function update(event: any) {
x.value = event.pageX;
y.value = event.pageY;
}
onMounted(() => window.addEventListener("mousemove", update));
onUnmounted(() => window.removeEventListener("mousemove", update));
</script>
<template>
<div class="container">鼠標坐標是: {{ x }}, {{ y }}</div>
</template>

組合函數(shù)提取,進行邏輯復用封裝
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 按照慣例,組合式函數(shù)名以“use”開頭
export function useMouse() {
// 被組合式函數(shù)封裝和管理的狀態(tài)
const x = ref(0)
const y = ref(0)
// 組合式函數(shù)可以隨時更改其狀態(tài)。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一個組合式函數(shù)也可以掛靠在所屬組件的生命周期上
// 來啟動和卸載副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通過返回值暴露所管理的狀態(tài)
return { x, y }
}
封裝后的使用方式:
<script lang="ts" setup>
import { useMouse } from "@/components/common/mouse.js";
const { x, y } = useMouse();
</script>
<template>
<div class="container">鼠標坐標是: {{ x }}, {{ y }}</div>
</template>
可以在組合式函數(shù)中使用所有的組合式 API。
組合式函數(shù)還可以嵌套多個組合式函數(shù)。
我們可以將添加和清除 DOM 事件監(jiān)聽器的邏輯也封裝進一個組合式函數(shù)中
// 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() 組合式函數(shù)可以被簡化為:
// 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 }
}
每一個調用 useMouse() 的組件實例會創(chuàng)建其獨有的 x、y 狀態(tài)拷貝,因此他們不會互相影響。
如果你想要在組件之間共享狀態(tài),后面回學到狀態(tài)管理。
###異步狀態(tài)示例
需要接收一個參數(shù)的組合式函數(shù)示例。在做異步數(shù)據(jù)請求時,我們常常需要處理不同的狀態(tài):加載中、加載成功和加載失敗。
// fetch.js
import { ref, watchEffect, toValue } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const fetchData = () => {
// reset state before fetching..
data.value = null
error.value = null
fetch(toValue(url))
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
}
watchEffect(() => {
fetchData()
})
return { data, error }
}
toValue() 是一個在 3.3 版本中新增的 API。它的設計目的是將 ref 或 getter 規(guī)范化為值。如果參數(shù)是 ref,它會返回 ref 的值;如果參數(shù)是函數(shù),它會調用函數(shù)并返回其返回值。否則,它會原樣返回參數(shù)。它的工作方式類似于 unref(),但對函數(shù)有特殊處理。
注意 toValue(url) 是在 watchEffect 回調函數(shù)的內部調用的。這確保了在 toValue() 規(guī)范化期間訪問的任何響應式依賴項都會被偵聽器跟蹤。
這個版本的 useFetch() 現(xiàn)在能接收靜態(tài) URL 字符串、ref 和 getter,使其更加靈活。watch effect 會立即運行,并且會跟蹤 toValue(url) 期間訪問的任何依賴項。如果沒有跟蹤到依賴項(例如 url 已經(jīng)是字符串),則 effect 只會運行一次;否則,它將在跟蹤到的任何依賴項更改時重新運行。
約定和最佳實踐
命名
組合式函數(shù)約定用駝峰命名法命名,并以“use”作為開頭。
輸入?yún)?shù)
即便不依賴于 ref 或 getter 的響應性,組合式函數(shù)也可以接收它們作為參數(shù)。如果你正在編寫一個可能被其他開發(fā)者使用的組合式函數(shù),最好處理一下輸入?yún)?shù)是 ref 或 getter 而非原始值的情況??梢岳?toValue() 工具函數(shù)來實現(xiàn):
import { toValue } from 'vue'
function useFeature(maybeRefOrGetter) {
// 如果 maybeRefOrGetter 是一個 ref 或 getter,
// 將返回它的規(guī)范化值。
// 否則原樣返回。
const value = toValue(maybeRefOrGetter)
}
如果你的組合式函數(shù)在輸入?yún)?shù)是 ref 或 getter 的情況下創(chuàng)建了響應式 effect,為了讓它能夠被正確追蹤,請確保要么使用 watch() 顯式地監(jiān)視 ref 或 getter,要么在 watchEffect() 中調用 toValue()。
返回值
你可能已經(jīng)注意到了,我們一直在組合式函數(shù)中使用 ref() 而不是 reactive()。我們推薦的約定是組合式函數(shù)始終返回一個包含多個 ref 的普通的非響應式對象,這樣該對象在組件中被解構為 ref 之后仍可以保持響應性:
// x 和 y 是兩個 ref
const { x, y } = useMouse()
從組合式函數(shù)返回一個響應式對象會導致在對象解構過程中丟失與組合式函數(shù)內狀態(tài)的響應性連接。與之相反,ref 則可以維持這一響應性連接。
如果你更希望以對象屬性的形式來使用組合式函數(shù)中返回的狀態(tài),你可以將返回的對象用 reactive() 包裝一次,這樣其中的 ref 會被自動解包,例如:
const mouse = reactive(useMouse()) // mouse.x 鏈接到了原來的 x ref console.log(mouse.x)
使用限制
組合式函數(shù)只能在 <script setup> 或 setup() 鉤子中被調用。在這些上下文中,它們也只能被同步調用。在某些情況下,你也可以在像 onMounted() 這樣的生命周期鉤子中調用它們。
這些限制很重要,因為這些是 Vue 用于確定當前活躍的組件實例的上下文。訪問活躍的組件實例很有必要,這樣才能:
將生命周期鉤子注冊到該組件實例上
將計算屬性和監(jiān)聽器注冊到該組件實例上,以便在該組件被卸載時停止監(jiān)聽,避免內存泄漏。
<script setup> 是唯一在調用 await 之后仍可調用組合式函數(shù)的地方。編譯器會在異步操作之后自動為你恢復當前的組件實例。
與其他模式的比較
和 Mixin 的對比
Vue 2 的用戶可能會對 mixins 選項比較熟悉。它也讓我們能夠把組件邏輯提取到可復用的單元里。然而 mixins 有三個主要的短板:
不清晰的數(shù)據(jù)來源:當使用了多個 mixin 時,實例上的數(shù)據(jù)屬性來自哪個 mixin 變得不清晰,這使追溯實現(xiàn)和理解組件行為變得困難。這也是我們推薦在組合式函數(shù)中使用 ref + 解構模式的理由:讓屬性的來源在消費組件時一目了然。
命名空間沖突:多個來自不同作者的 mixin 可能會注冊相同的屬性名,造成命名沖突。若使用組合式函數(shù),你可以通過在解構變量時對變量進行重命名來避免相同的鍵名。
隱式的跨 mixin 交流:多個 mixin 需要依賴共享的屬性名來進行相互作用,這使得它們隱性地耦合在一起。而一個組合式函數(shù)的返回值可以作為另一個組合式函數(shù)的參數(shù)被傳入,像普通函數(shù)那樣。
基于上述理由,我們不再推薦在 Vue 3 中繼續(xù)使用 mixin。保留該功能只是為了項目遷移的需求和照顧熟悉它的用戶。
和無渲染組件的對比
在組件插槽一章中,我們討論過了基于作用域插槽的無渲染組件。我們甚至用它實現(xiàn)了一樣的鼠標追蹤器示例。
組合式函數(shù)相對于無渲染組件的主要優(yōu)勢是:組合式函數(shù)不會產(chǎn)生額外的組件實例開銷。當在整個應用中使用時,由無渲染組件產(chǎn)生的額外組件實例會帶來無法忽視的性能開銷。
我們推薦在純邏輯復用時使用組合式函數(shù),在需要同時復用邏輯和視圖布局時使用無渲染組件。
和 React Hooks 的對比
如果你有 React 的開發(fā)經(jīng)驗,你可能注意到組合式函數(shù)和自定義 React hooks 非常相似。組合式 API 的一部分靈感正來自于 React hooks,Vue 的組合式函數(shù)也的確在邏輯組合能力上與 React hooks 相近。然而,Vue 的組合式函數(shù)是基于 Vue 細粒度的響應性系統(tǒng),這和 React hooks 的執(zhí)行模型有本質上的不同。這一話題在組合式 API 的常見問題中有更細致的討論。
到此這篇關于vue3 邏輯復用的實現(xiàn)示例的文章就介紹到這了,更多相關vue3 邏輯復用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Vue +WebSocket + WaveSurferJS 實現(xiàn)H5聊天對話交互的實例
這篇文章主要介紹了Vue +WebSocket + WaveSurferJS 實現(xiàn)H5聊天對話交互的實例,幫助大家更好的理解和學習vue,感興趣的朋友可以了解下2020-11-11

