什麼是組合式函數
在 Vue 應用程式的概念中,「組合式函數」(Composables) 是利用 Vue 的組合式 API 來封裝和重複使用有狀態邏輯的函數。
當建構前端應用時,我們常常需要重複使用公共任務的邏輯。例如為了在不同地方格式化時間,我們可能會抽取一個可重複使用的日期格式化函數。這個函數封裝了無狀態的邏輯:它在接收一些輸入後立刻回傳所期望的輸出。重複使用無狀態邏輯的函式庫有很多,例如你可能已經用過的lodash或是date-fns。
相較之下,有狀態邏輯負責管理會隨時間而改變的狀態。一個簡單的例子是追蹤當前滑鼠在頁面中的位置。在實際應用中,也可能是像是觸控手勢或與資料庫的連線狀態這樣的更複雜的邏輯。
滑鼠追蹤器範例
如果我們要直接在元件中使用組合式API 實作滑鼠追蹤功能,它會是這樣的:
<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>
如你所見,核心邏輯完全一致,我們所做的只是把它移到一個外部函數中去,並返回需要暴露的狀態。和在元件中一樣,你也可以在組合式函數中使用所有的組合 API。現在,useMouse()
的功能可以在任何元件中輕易重複使用了。
更酷的是,你還可以嵌套多個組合式函數:一個組合式函數可以呼叫一個或多個其他的組合式函數。這使得我們可以像使用多個元件組合成整個應用一樣,用多個較小且邏輯獨立的單元來組合形成複雜的邏輯。實際上,這正是為什麼我們決定將實作了這個設計模式的 API 集合命名為組合 API。
舉例來說,我們可以將新增和清除DOM 事件監聽器的邏輯也封裝進一個組合式函數中:
// 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 } }
TIP
每一個呼叫useMouse()
的元件實例會建立其獨特的x
、y
狀態拷貝,因此他們不會互相影響。
非同步狀態範例
useMouse()
組合式函數沒有接收任何參數,因此讓我們再來看一個需要接收一個參數的組合式函數範例。在做非同步資料請求時,我們常常需要處理不同的狀態:載入中、載入成功和載入失敗。
<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 class="brush:php;toolbar:false">{{ data }}
如果在每個需要取得資料的元件中都要重複這種模式,那就太繁瑣了。讓我們把它抽取成一個組合式函數:
// 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()
接收一個靜態的URL 字串作為輸入,所以它只執行一次請求,然後就完成了。但如果我們想讓它在每次 URL 變更時都重新要求呢?那我們可以讓它同時允許接收 ref 作為參數:
// fetch.js import { ref, isRef, unref, watchEffect } from 'vue' 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 'vue' function useFeature(maybeRef) { // 若 maybeRef 确实是一个 ref,它的 .value 会被返回 // 否则,maybeRef 会被原样返回 const value = unref(maybeRef) }
如果你的組合函數在接收ref 為參數時會產生響應式effect,請確保使用watch()
顯式地監聽此ref,或在watchEffect()
中呼叫unref()
來進行正確的追蹤。
傳回值
你可能已經注意到了,我們一直在組合式函數中使用ref()
而不是reactive()
。我們推薦的約定是組合式函數總是會傳回一個包含多個ref 的普通的非響應式對象,這樣該物件在元件中被解構為ref 之後仍可以保持響應性:
js
// x 和 y 是两个 ref const { x, y } = useMouse()
從組合式函數傳回一個響應式物件會導致在物件解構過程中遺失與組合式函數內狀態的響應性連接。與之相反,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 './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>
在某种程度上,你可以将这些提取出的组合式函数看作是可以相互通信的组件范围内的服务。
选项式API中使用组合式函数
如果你正在使用选项式 API,组合式函数必须在setup()
中调用。且其返回的绑定必须在setup()
中返回,以便暴露给this
及其模板:
import { useMouse } from './mouse.js' import { useFetch } from './fetch.js' export default { setup() { const { x, y } = useMouse() const { data, error } = useFetch('...') 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中文網其他相關文章!

Vue.js和React的未來趨勢和預測分別是:1)Vue.js將在企業級應用中廣泛應用,並在服務端渲染和靜態站點生成方面有突破;2)React將在服務器組件和數據獲取方面創新,並進一步優化並發模式。

Netflix的前端技術棧主要基於React和Redux。 1.React用於構建高性能的單頁面應用,通過組件化開發提升代碼重用性和維護性。 2.Redux用於狀態管理,確保狀態變化可預測和可追踪。 3.工具鏈包括Webpack、Babel、Jest和Enzyme,確保代碼質量和性能。 4.性能優化通過代碼分割、懶加載和服務端渲染實現,提升用戶體驗。

Vue.js是一種漸進式框架,適用於構建交互性強的用戶界面。其核心功能包括響應式系統、組件化開發和路由管理。 1)響應式系統通過Object.defineProperty或Proxy實現數據監聽,自動更新界面。 2)組件化開發允許將界面拆分為可複用的模塊。 3)VueRouter支持單頁面應用,提升用戶體驗。

Vue.js的主要缺點包括:1.生態系統相對較新,第三方庫和工具不如其他框架豐富;2.學習曲線在復雜功能上變得陡峭;3.社區支持與資源不如React和Angular廣泛;4.大型應用中可能遇到性能問題;5.版本升級與兼容性挑戰較大。

Netflix使用React作為其前端框架。 1.React的組件化開發和虛擬DOM機制提高了性能和開發效率。 2.使用Webpack和Babel優化代碼構建和部署。 3.採用代碼分割、服務端渲染和緩存策略進行性能優化。

Vue.js受歡迎的原因包括簡單易學、靈活性高和高效性能。 1)其漸進式框架設計適合初學者逐步學習。 2)組件化開發提高了代碼可維護性和團隊協作效率。 3)響應式系統和虛擬DOM提升了渲染性能。

Vue.js更易用且學習曲線較平緩,適合初學者;React學習曲線較陡峭,但靈活性強,適合有經驗的開發者。 1.Vue.js通過簡單的數據綁定和漸進式設計易於上手。 2.React需要理解虛擬DOM和JSX,但提供更高的靈活性和性能優勢。

Vue.js適合快速開發和小型項目,而React更適合大型和復雜的項目。 1.Vue.js簡單易學,適用於快速開發和小型項目。 2.React功能強大,適合大型和復雜的項目。 3.Vue.js的漸進式特性適合逐步引入功能。 4.React的組件化和虛擬DOM在處理複雜UI和數據密集型應用時表現出色。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Linux新版
SublimeText3 Linux最新版

禪工作室 13.0.1
強大的PHP整合開發環境

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。