讓Composition API 存在于 Vue3.0 項目以外
- 作者:陳大魚頭
- github:KRISACHAN
作為新特性 Composition API ,在 Vue3 正式發(fā)布之前一段時間就發(fā)布過了。
據(jù)文檔介紹, Composition API 是一組低侵入式的、函數(shù)式的 API,使得我們能夠更靈活地「組合」組件的邏輯。
不僅在 Vue 中,在其他的框架或原生 JS 也可以很好地被使用,下面我們就選取幾個比較重要的 Composition API ,通過一些簡單的例子來看看如何在其他項目中使用。
注:本文僅列出各個分類下比較重要的 API,想要查看全部可以點擊下方鏈接進行查看:
https://github.com/vuejs/vue-next/tree/master/packages/reactivity
reactive API
createReactiveObject
createReactiveObject函數(shù)是 reactive API 的核心,用于創(chuàng)建 ?reactive對象 。
在分享 API 之前,我們先看看其核心實現(xiàn),由于篇幅有限,本文僅展示出理解后的簡化版代碼,代碼如下:
function?createReactiveObject(
??target,?//?要監(jiān)聽目標
??isReadonly,?//?是否只讀
??baseHandlers,?//?target?為?Object?或?Array?時的處理器,支持對數(shù)據(jù)的增刪改查
??collectionHandlers?//?target?為?Map/WeakMap?或?Set/WeakSet?時的處理器,支持對數(shù)據(jù)的增刪改查
)?{
????if?(target[ReactiveFlags.RAW]?&&?!(isReadonly?&&?target[ReactiveFlags.IS_REACTIVE])?{
??????//?當?target?已經(jīng)是一個?Proxy?時,直接返回
??????//?例外情況:在 Proxy 里調(diào)用 readonly()
?????return?target
????}
????//?當前對象已被監(jiān)聽過時,就直接返回被監(jiān)聽的對象
????if?(existingProxy)?{
??????return?existingProxy
????}
????//?如果是?Object?Array?Map/WeakMap?Set/WeakSet?以外只能監(jiān)聽到值的數(shù)據(jù),直接返回
????if?(targetType?===?TargetType.INVALID)?{
??????return?target
????}
????//?根據(jù)參數(shù)類型生成對應(yīng)的?Proxy?對象,以及添加對應(yīng)的處理器
????const?proxy?=?new?Proxy(
??????target,
??????targetType?===?TargetType.COLLECTION???collectionHandlers?:?baseHandlers
????)
????proxyMap.set(target,?proxy)
????return?proxy
}
reactive
接收一個普通對象然后返回該普通對象的響應(yīng)式代理。等同于 2.x 的
Vue.observable()
示例如下:
import?{
??reactive,
??isReactive?//?判斷是否是?reactive?對象
}?from?'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
const?obj?=?{
??nested:?{
????foo:?1
??},
??array:?[{?bar:?2?}]
}
const?value?=?10
const?observedObj?=?reactive(obj)
const?observedValue?=?reactive(value)
console.log(isReactive(observedObj))?//?true
console.log(isReactive(observedValue))?//?true
shallowReactive
只為某個對象的私有(第一層)屬性創(chuàng)建淺層的響應(yīng)式代理,不會對“屬性的屬性”做深層次、遞歸的響應(yīng)式代理,而只是保留原樣。
示例如下:
const?obj?=?{
??nested:?{
????foo:?1
??},
??array:?[{?bar:?2?}]
}
const?value?=?10
const?unobservedObj?=?shallowReactive(obj)
const?unobservedValue?=?shallowReactive(value)
console.log(isReactive(observedObj))?//?false
console.log(isReactive(observedValue))?//?false
effect API
createReactiveEffect
createReactiveEffect是 effect API 的核心,用于創(chuàng)建監(jiān)聽用戶自定義的reactive對象的函數(shù)
在分享 API 之前,我們先看看其核心實現(xiàn),由于篇幅有限,本文僅展示出理解后的簡化版代碼,代碼如下:
function?createReactiveEffect(
?fn,?//?用戶創(chuàng)建的?reactive?對象變動執(zhí)行回調(diào)
??options?=?{
????lazy,?//?是否執(zhí)行用戶函數(shù)
????scheduler,?//?收集數(shù)據(jù)記錄
????onTrack,?//?追蹤用戶數(shù)據(jù)的回調(diào)
????onTrigger,?//?追蹤變更記錄
????onStop,?//?停止執(zhí)行
????allowRecurse?//?是否允許遞歸
??}
)?{
????const?effect?=?function?reactiveEffect()?{
??????if?(!effectStack.includes(effect))?{
????????cleanup(effect)?//?清空?effect
????????try?{
??????????enableTracking()?//?往追蹤用戶數(shù)據(jù)的棧內(nèi)添加當前?effect
??????????effectStack.push(effect)?//?往?effect?棧內(nèi)添加?effect
??????????activeEffect?=?effect?//?將活動?effect?變成當前?effect
??????????return?fn()?//?執(zhí)行回調(diào)
????????}?finally?{?//?刪除當前記錄
??????????effectStack.pop()
??????????resetTracking()
??????????activeEffect?=?effectStack[effectStack.length?-?1]
????????}
??????}
????}
??effect.id?=?uid++
????effect._isEffect?=?true
????effect.active?=?true
????effect.raw?=?fn
????effect.deps?=?[]
????effect.options?=?options
????return?effect
}
effect
effect函數(shù)是 effect API 的核心。以WeakMap為數(shù)據(jù)類型,是一個用于存儲用戶自定義函數(shù)的 訂閱者。
示例如下:
let?dummy
const?counter?=?reactive({?num:?0?})
effect(()?=>?(dummy?=?counter.num))
console.log(dummy?===?0)?//?true
counter.num?=?7
console.log(dummy?===?7)?//?true
ref API
RefImpl
RefImpl是 ref API 的核心,用于創(chuàng)建ref對象
在分享 API 之前,我們先看看其核心實現(xiàn),代碼如下:
class?RefImpl?{
??private?_value?//?存儲當前?ref?對象的值
??public?__v_isRef?=?true?//?確定是否為?ref?對象的變量?(只讀)
??constructor(
????private?_rawValue,?//?用戶傳入的原始值
????public?readonly?_shallow?=?false?//?當前?ref?對象是否為?shallowRef
??)?{
????// convert:如果傳入的原始值為對象,則會被 convert 函數(shù)轉(zhuǎn)換為 reactive 對象
????this._value?=?_shallow???_rawValue?:?convert(_rawValue)
??}
??get?value()?{
????//?用于追蹤用戶輸入的值變化
????// track:effect api 的 track 函數(shù),用于追蹤用戶行為,當前則是追蹤用戶的 get 操作
????// toRaw:effect api 的 toRaw 函數(shù),將 this 轉(zhuǎn)化為用戶輸入值
????track(toRaw(this),?TrackOpTypes.GET,?'value')
????return?this._value
??}
??set?value(newVal)?{
????if?(hasChanged(toRaw(newVal),?this._rawValue))?{
??????//?當前?ref?對象有變化時
??????//?_rawValue?/?_value?變更
??????// trigger:effect api 的 trigger 函數(shù),根據(jù)用戶傳入的值與操作類型來進行操作,當前則為將用戶傳入的值添加到值 map 里
??????this._rawValue?=?newVal
??????this._value?=?this._shallow???newVal?:?convert(newVal)
??????trigger(toRaw(this),?TriggerOpTypes.SET,?'value',?newVal)
????}
??}
}
ref
接受一個參數(shù)值并返回一個響應(yīng)式且可改變的 ref 對象。ref 對象擁有一個指向內(nèi)部值的單一屬性
.value。如果傳入 ref 的是一個對象,將調(diào)用reactive方法進行深層響應(yīng)轉(zhuǎn)換。
示例如下:
const?count?=?ref({
??name:?'魚頭',
??type:?'帥哥'
})
console.log(count.value.type)?//?帥哥
count.value.type?=?'超級大帥哥'
console.log(count.value.type)?//?超級大帥哥
shallowRef
創(chuàng)建一個 ref ,將會追蹤它的
.value更改操作,但是并不會對變更后的.value做響應(yīng)式代理轉(zhuǎn)換(即變更不會調(diào)用reactive)
示例如下:
const?__shallowRef?=?shallowRef({?a:?1?})
let?dummy
effect(()?=>?{
??dummy?=?__shallowRef.value.a
})
console.log(dummy)?//?1
__shallowRef.value.a?=?2
console.log(dummy)?//?1
console.log(isReactive(__shallowRef.value))?//?false
customRef
customRef用于自定義一個ref,可以顯式地控制依賴追蹤和觸發(fā)響應(yīng),接受一個工廠函數(shù),兩個參數(shù)分別是用于追蹤的track與用于觸發(fā)響應(yīng)的trigger,并返回一個帶有get和set屬性的對象。
示例如下:
let?value?=?1
let?_trigger
const?custom?=?customRef((track,?trigger)?=>?({
??get()?{
????track()
????return?value
??},
??set(newValue)?{
????value?=?newValue
????_trigger?=?trigger
??}
}))
let?dummy
effect(()?=>?{
??dummy?=?custom.value
})
console.log(dummy)?//?1
custom.value?=?2
console.log(dummy)?//?1
_trigger()
console.log(dummy)?//?2
triggerRef
triggerRef用于主動觸發(fā)shallowRef
示例如下:
const?__shallowRef?=?shallowRef({?a:?1?})
let?dummy
effect(()?=>?{
??dummy?=?__shallowRef.value.a
})
console.log(dummy)?//?1
__shallowRef.value.a?=?2
console.log(dummy)?//?1
console.log(isReactive(__shallowRef.value))?//?false
triggerRef(__shallowRef)
console.log(dummy)?//?2
computed API
ComputedRefImpl
ComputedRefImpl是 ref API 的核心,用于創(chuàng)建computed對象
在分享 API 之前,我們先看看其核心實現(xiàn),由于篇幅有限,本文僅展示出理解后的簡化版代碼,代碼如下:
class?ComputedRefImpl?{
??private?_value?//?當前值
??private?_dirty?=?true?//?當前值是否發(fā)生過變更
??public?effect?//?effect?對象
??public?__v_isRef?=?true;?//?指定為?ref?對象
??public?[ReactiveFlags.IS_READONLY]:?boolean?//?是否只讀
??constructor(
????getter,?//?getter
????private?_setter,?//?setter
????isReadonly?//?是否只讀
??)?{
????this.effect?=?effect(getter,?{
??????lazy:?true,
??????scheduler:?()?=>?{
????????if?(!this._dirty)?{
??????????//?將變更狀態(tài)變?yōu)?true
??????????// trigger:effect api 的 trigger 函數(shù),根據(jù)用戶傳入的值與操作類型來進行操作,當前則為將用戶傳入的值添加到值 map 里
??????????this._dirty?=?true
??????????trigger(toRaw(this),?TriggerOpTypes.SET,?'value')
????????}
??????}
????})
????this[ReactiveFlags.IS_READONLY]?=?isReadonly
??}
??get?value()?{
????if?(this._dirty)?{
??????//?返回當前值
??????//?將變更狀態(tài)變?yōu)?false
??????this._value?=?this.effect()
??????this._dirty?=?false
????}
????// track:effect api 的 track 函數(shù),用于追蹤用戶行為,當前則是追蹤用戶的 get 操作
????track(toRaw(this),?TrackOpTypes.GET,?'value')
????return?this._value
??}
??set?value(newValue)?{
????this._setter(newValue)
??}
}
computed
傳入一個 getter 函數(shù),返回一個默認不可手動修改的 ref 對象。或者傳入一個擁有
get和set函數(shù)的對象,創(chuàng)建一個可手動修改的計算狀態(tài)。
示例如下:
const?count1?=?ref(1)
const?plus1?=?computed(()?=>?count1.value?+?1)
console.log(plus1.value)?//?2
plus1.value++?//?Write?operation?failed:?computed?value?is?readonly
const?count2?=?ref(1)
const?plus2?=?computed({
??get:?()?=>?count2.value?+?1,
??set:?val?=>?{
????count2.value?=?val?-?1
??}
})
console.log(plus2.value)?//?2
plus2.value?=?0
console.log(plus2.value)?//?0
??看完三件事
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:
- 點贊,讓更多的人也能看到介紹內(nèi)容(收藏不點贊,都是耍流氓-_-)
- 關(guān)注公眾號“前端勸退師”,不定期分享原創(chuàng)知識。
- 也看看其他文章
勸退師個人微信:huab119
也可以來我的GitHub博客里拿所有文章的源文件:
前端勸退指南:https://github.com/roger-hiro/BlogFN一起玩耍呀

