zone.js 由入門到放棄之五 ——NgZone & ApplicationRef 源碼分析

嘯達(dá)同學(xué)剛寫 zone.js 系列就說過,NgZone 影響著 Angular 中的變更檢測(cè),歷時(shí)一個(gè)多月的筆耕不輟,終于到了他初次下筆時(shí)的目的地~
zone.js 系列
zone.js 由入門到放棄之一?—— 通過一場(chǎng)游戲認(rèn)識(shí) zone.js
zone.js 由入門到放棄之二 ——zone.js API 大練兵
zone.js 由入門到放棄之三 ——zone.js 源碼分析【setTimeout 篇】
zone.js 由入門到放棄之四 ——Angular 對(duì) zone.js 的應(yīng)用
初見 NgZone
其實(shí)在上一篇文章中,大家已經(jīng)初步窺探過 NgZone 的芳容了。而且我們也知道了,在 NgZone 中維護(hù)了 OuterZone 和 InnerZone 兩個(gè) Zone。今天的這篇文章,我們主要分析一下 InnerZone,并看一下 InnerZone 是如何跟 Angular 的變更檢測(cè)聯(lián)系到一起的。

InnerZone 四方法
NgZone 中 InnerZone 的創(chuàng)建是通過?forkInnerZoneWithAngularBehavior
?完成的,創(chuàng)建過程的簡化版如下,其中又能看到很多熟悉的勾子函數(shù)。這里簡單復(fù)習(xí)一下這幾個(gè)勾子的意義:
onInvokeTask
:zone.js 會(huì)在初始化的時(shí)候?qū)惒椒椒ǘ?Pathc 成 ZoneTask,從而跟蹤異步任務(wù)的執(zhí)行情況的。onInvokeTask
?就是其中的一個(gè)勾子函數(shù),它會(huì)在異步任務(wù)執(zhí)行回調(diào)的時(shí)候觸發(fā)。onInvoke
:onInvoke
?會(huì)在我們手動(dòng)執(zhí)行 zone.run () 的時(shí)候執(zhí)行。onHasTask
:是針對(duì)整個(gè)任務(wù)隊(duì)列狀態(tài)改變的監(jiān)聽,當(dāng)檢測(cè)任務(wù)隊(duì)列中有任務(wù)進(jìn)入、或是有任務(wù)執(zhí)行完出隊(duì)列的時(shí)候會(huì)被執(zhí)行。onHandleError
:當(dāng)有異常拋出時(shí)被執(zhí)行
InnerZone 對(duì)異步任務(wù)的控制精華基本上就全部濃縮在這幾個(gè)勾子函數(shù)中了,與此同時(shí),為了更好地配合對(duì)異步任務(wù)的跟蹤,NgZone 中還定義了很多狀態(tài)監(jiān)控字段。只有理清這些字段的含義才能繼續(xù)往下深入代碼。
不熟悉 zone.js 原理的可以回看一下 zone.js 由入門到放棄之一和 zone.js 由入門到放棄之二(鏈接見文首)
傳送門
InnerZone 五狀態(tài)
接下來這幾個(gè)狀態(tài)屬性會(huì)貫穿在后面的源碼分析的全部過程中,我們也會(huì)通過對(duì)這幾個(gè)狀態(tài)的跟蹤了解一下 InnerZone 事件跟蹤的原理。
hasPendingMacrotasks: boolean 隊(duì)列中是否有待執(zhí)行的宏任務(wù)
hasPendingMicrotasks: boolean 隊(duì)列中是否有待執(zhí)行的微任務(wù)
_nesting: number 隊(duì)列中待執(zhí)行任務(wù)的個(gè)數(shù)
isStable: boolean 當(dāng)任務(wù)隊(duì)列中既沒有待執(zhí)行的宏任務(wù),也沒有待執(zhí)行的微任務(wù)時(shí),isStable 為 ture,表示當(dāng)前是個(gè)穩(wěn)定的狀態(tài)。反之則代表非穩(wěn)定狀態(tài)。
lastRequestAnimationFrameId: number 這個(gè)狀態(tài)有些特別,它是一個(gè)延時(shí)器,后面會(huì)展開解釋。
代碼走讀
前面在介紹 zone.js 的時(shí)候我們說過,zone.js 把異步任務(wù)分為 MacroTask、MicroTask 和 Event 三種。今天我們就分別把這三種任務(wù)都按流程分析一遍。從難易程度上看,MacroTask 最簡單,Event 相對(duì)最復(fù)雜。接下來,我們就按照這個(gè)順序講解。

MacroTask
之前在 zone.js 由入門到放棄之三中,詳細(xì)介紹過 zone.js 對(duì) setTimeout 的 Patch 過程,如果不了解具體過程的強(qiáng)烈建議先瀏覽一下那篇文章。
這一次,我們還是通過個(gè) setTimeout 事件來跟蹤 NgZone 的處理過程,測(cè)試代碼很簡單,如下所示。
因?yàn)?zone.js 可以感知到任務(wù)隊(duì)列的變化情況,所以當(dāng)?setTimeout
?執(zhí)行時(shí),它可以知道當(dāng)前有一個(gè)宏任務(wù)來了,同時(shí)會(huì)觸發(fā) onHasTask 勾子。
onHasTask
當(dāng)?onHasTask
"檢測(cè)" 到有宏任務(wù)到來時(shí),會(huì)把?hasPendingMacrotasks
?設(shè)置為 true。
傳送門
此時(shí),NgZone 中的幾個(gè)狀態(tài)值大概是這個(gè)樣子的,hasPendingMacrotasks 變?yōu)?true,表示當(dāng)前有一個(gè)待執(zhí)行的 MacroTask。
接下來,zone.js 會(huì)通過調(diào)用?scheduleFn
,并把封裝后的回調(diào)函數(shù)放在 Timer 隊(duì)列中等待時(shí)鐘到達(dá)。
hasPendingMacrotaskshasPendingMicrotasks_nestingisStablelastRequestAnimationFrameIdtruefalse0true-1
onInvokeTask
當(dāng)時(shí)鐘到達(dá)以后,事件循環(huán)會(huì)把封裝后的回調(diào)函數(shù)放在任務(wù)隊(duì)列中等待執(zhí)行。當(dāng)執(zhí)行到回調(diào)時(shí),回調(diào)會(huì)觸發(fā)?task.invoke
?函數(shù),接下來就會(huì)喚醒 onInvokeTask 勾子函數(shù)。
傳送門
delegate.invokeTask(target, task, applyThis, applyArgs);
?是用來調(diào)用真正的回調(diào)函數(shù)的。除了這行,我們可以看到在回調(diào)之前先后分別還各有一個(gè)方法:onEnter
?和?onLeave
。
onEnter
onEnter
?執(zhí)行過程中,_nesting
?會(huì)自增,表示了當(dāng)前新增一個(gè)待執(zhí)行任務(wù)。當(dāng)有任務(wù)要執(zhí)行時(shí),之前的穩(wěn)定狀態(tài)會(huì)被打破,同時(shí)觸發(fā)一個(gè)?onUnstable
?事件。這個(gè)?onUnstable
?事件被 ApplicationRef 訂閱,ApplicationRef 會(huì)根據(jù)這個(gè)事件同步修改它自身的穩(wěn)定狀態(tài)(ApplicationRef 的代碼后面講解)。
傳送門

onLeave
onLeave
?函數(shù)執(zhí)行的時(shí)候,說明 MarcoTask 的回調(diào)已經(jīng)執(zhí)行完畢,_nesting
?會(huì)執(zhí)行一次自減操作。接下來又執(zhí)行了?checkStable
?函數(shù)。
傳送門
checkStable
?函數(shù)非常關(guān)鍵!每當(dāng)執(zhí)行到 checkStable 的時(shí)候,都是變更檢測(cè)執(zhí)行的關(guān)鍵。以至于這個(gè)函數(shù)的每一行都值得拿出來講一下,我在代碼中標(biāo)記了序號(hào),這樣方便后面走讀代碼。
checkStable
?既然是判斷是否進(jìn)行變更檢測(cè)關(guān)鍵,那么 1 標(biāo)識(shí)的 if 子句就是判斷的關(guān)鍵。代碼的意思大概就是,只有確保當(dāng)前沒有任何待執(zhí)行的任務(wù),同時(shí)當(dāng)前狀態(tài)為不穩(wěn)定狀態(tài)的時(shí)候才需要觸發(fā)變更檢測(cè)。代碼 2 標(biāo)識(shí)了一個(gè)成對(duì)的
_nesting
?自增、自減操作。這里這么做的原因是代碼 3 這里拋出了事件,對(duì)該事件的訂閱實(shí)際上也是一個(gè)異步任務(wù)。所以這里通過_nesting
?的自增、自減操作說明這里是有一個(gè)異步任務(wù)的。代碼 3 就是變更檢測(cè)的關(guān)鍵了,AppliactionRef 會(huì)訂閱?
onMicrotaskEmpty
?事件,每當(dāng)?onMicrotaskEmpty
?觸發(fā)后,AppliactionRef 就會(huì)執(zhí)行一次變更檢測(cè)。代碼 4 這里大家可能會(huì)有疑問,為什么在這里還要對(duì)?
hasPendingMicrotasks
?進(jìn)行一次判斷?這是因?yàn)樵诖a 3 這里,對(duì)?onMicrotaskEmpty
?的訂閱者有可能會(huì)在訂閱回調(diào)中再執(zhí)行一些異步任務(wù),就像下面這樣。此時(shí),并不能保證在?checkStable
?的過程中,不會(huì)有新的任務(wù)進(jìn)入到待執(zhí)行隊(duì)列。所以這里,又對(duì)?hasPendingMicrotasks
?的狀態(tài)做了一次判斷。確保在狀態(tài)變?yōu)榉€(wěn)定之前,任務(wù)隊(duì)列中不存在任務(wù)微任務(wù)。
5. 代碼 5 是對(duì)外觸發(fā)一個(gè)狀態(tài)穩(wěn)定的事件,這個(gè)事件跟?OnEnter
?函數(shù)中那個(gè)?onUnstable
?相對(duì)。但是你可能會(huì)好奇,這里為什么要在?runOutsideAngular
?中執(zhí)行。我這里解釋下,僅代表個(gè)人見解。onStable
?和?onMicrotaskEmpty
?存在一樣的問題,因?yàn)槎际强捎^察對(duì)象,所以存在訂閱者在回調(diào)繼續(xù)執(zhí)行異步任務(wù)的問題。如果在?onStable
?的訂閱中執(zhí)行異步任務(wù),那 NgZone 的狀態(tài)馬上有會(huì)變成非穩(wěn)定的,這將會(huì)陷入一個(gè)無限的死循環(huán)中,NgZone 會(huì)在穩(wěn)定和不穩(wěn)定狀態(tài)之間來回切換,永不停止。所以這里使用?runOutsideAngular
,讓 zone.js 放棄對(duì)這里的代碼進(jìn)行跟蹤。這樣,根據(jù)上一講我們學(xué)過的內(nèi)容,runOutsideAngular
?中執(zhí)行異步不會(huì)觸發(fā)變更檢測(cè),當(dāng)然也不會(huì)觸發(fā) NgZone 的狀態(tài)變化。
6. 改變 zone 的狀態(tài)為穩(wěn)定。
傳送門
這里我多補(bǔ)充一點(diǎn)知識(shí),我之前看到這里的代碼的時(shí)候也覺得有點(diǎn)繞,所以我在這里做了大量的測(cè)試。結(jié)果發(fā)現(xiàn),如果在 onStable 的訂閱回調(diào)中再使用 zone.run 執(zhí)行異步任務(wù)的時(shí)候就會(huì)造成一個(gè)無限的死循環(huán)。這里是我的最小實(shí)現(xiàn)倉,夠膽的可以試試,你的瀏覽器會(huì)在瞬間崩潰。當(dāng)然,我也給官方提了?issue,原作者也證實(shí)了這的確是個(gè)問題,感興趣的可以跟蹤一下,持續(xù)關(guān)注。
截止到這里,我們?cè)倏匆幌?NgZone 的幾個(gè)狀態(tài)指標(biāo)。此時(shí)隊(duì)列中不存在待執(zhí)行的任務(wù),NgZone 會(huì)把自身狀態(tài)修改為穩(wěn)定態(tài)。

onHasTask
整個(gè) setTimeout 跟蹤的最后一步還是這個(gè)勾子,這次,勾子函數(shù)中會(huì)把 hasPendingMacrotasks 置為 false。此時(shí),幾個(gè)狀態(tài)已經(jīng)恢復(fù)為最初的問題狀態(tài),Angular 也在這個(gè)過程中執(zhí)行了一次變更監(jiān)測(cè)。

MicroTask
MicroTask 和 MacroTask 的行為大致上一致,只不過由于 zone.js 在處理 MicroTask 和 MacroTask 時(shí)有一丟丟的區(qū)別,導(dǎo)致這里處理也會(huì)有一點(diǎn)不同,這個(gè)我會(huì)在下文做專門解釋。當(dāng)然,如果你還想關(guān)注 zone.js 在處理 MicroTask 和 MacroTask 時(shí)到底有什么不一樣的,可以關(guān)注一下我的下一篇文章(如果有的話),里面會(huì)像本系列的第三期一樣,詳細(xì)解釋 zone.js 處理 Promise 的技術(shù)細(xì)節(jié)。
onHasTask
onHasTask 跟之前沒什么區(qū)別,執(zhí)行過后,狀態(tài)如下。與 MacroTask 不同,這次是 hasPendingMicrotasks 變?yōu)?true,表示隊(duì)列中有一個(gè)待執(zhí)行的微任務(wù)。
傳送門

onInvokeTask
MicroTask 和 MacroTask 在這個(gè)勾子中的處理過程基本上是相同的。但是 MicroTask 在回調(diào)執(zhí)行的時(shí)候和 MacroTask 還是有一點(diǎn)差異的。前面部分,我們講 MacroTask 的時(shí)候,delegate.invokeTask(target, task, applyThis, applyArgs);
?這句會(huì)直接觸發(fā) setTimeout 的回調(diào)函數(shù)執(zhí)行。但是在 MicroTask 中,微任務(wù)的回調(diào)外部還會(huì)包裝一層?zone.run
,導(dǎo)致 MicroTask 的回調(diào)會(huì)通過?onInvoke
?勾子執(zhí)行。
傳送門
onInvoke
可以看到?onInvoke
?和?onInvokeTask
?函數(shù)的內(nèi)容是差不多的。onEnter
?和?onLeave
?的調(diào)用也基本一致,所以這里就不專門分析了。
當(dāng)這個(gè)函數(shù)執(zhí)行結(jié)束后,幾個(gè)狀態(tài)值變化如下。

onHasTask
最后一個(gè)執(zhí)行的還是?onHasTask
?函數(shù),這個(gè)函數(shù)執(zhí)行完畢后,幾個(gè)狀態(tài)又回到初始狀態(tài)。

Event
Event 的執(zhí)行方式跟 MacroTask 和 MicroTask 都不太一樣。還記得之前我們?cè)谥v NgZone 的 5 大狀態(tài)的時(shí)候,有一個(gè)?lastRequestAnimationFrameId
?一直沒有用到。那么,在 Event 的處理過程中,我們會(huì)看到它的作用。

onInvokeTask
Event 的處理入口是?onInvokeTask
?而不是?onHasTask
,onEnter
?和?delegate.invokeTask
?與之前都差不多,但是在 finally 子句中,你會(huì)發(fā)現(xiàn) Event 的處理中多了一個(gè)?delayChangeDetectionForEventsDelegate
?函數(shù)。其實(shí)從函數(shù)的函數(shù)名大概能猜個(gè)七七八八,這個(gè)是一個(gè)事件延時(shí)處理的函數(shù)。
delayChangeDetectionForEventsDelegate
其實(shí)我們?cè)谏弦黄谥v解中已經(jīng)介紹了一些通過 NgZone 進(jìn)行性能調(diào)優(yōu)的手段,那么這個(gè)函數(shù)的產(chǎn)生實(shí)際上也是用于性能上的優(yōu)化。我們知道,瀏覽器很多事件諸如 mousemove、scroll 這些都會(huì)在短時(shí)間內(nèi)產(chǎn)生大量的事件。如果每個(gè)這樣的事件都會(huì)觸發(fā)一次 Angular 的變更檢測(cè)的話,那么對(duì)性能上的要求是很大的。所以,NgZone 也需要在內(nèi)部對(duì)于這些瀏覽器的事件做一些特殊處理,讓大量的事件積攢一段時(shí)間后再統(tǒng)一做一次變更檢測(cè)。
那么?delayChangeDetectionForEventsDelegate
?中實(shí)際調(diào)用的方法是?delayChangeDetectionForEvents
,所以我們重點(diǎn)關(guān)注一下?delayChangeDetectionForEvents
?函數(shù)的源碼。
代碼 1 這里,我們第一次見到對(duì) lastRequestAnimationFrameId 的判斷,當(dāng)?shù)谝粋€(gè) Event 到來時(shí),這里的 lastRequestAnimationFrameId 還是初始值 - 1

zone.nativeRequestAnimationFrame
?的調(diào)用實(shí)際上調(diào)用的是?Window.requestAnimationFrame
。這里,NgZone 實(shí)際上是希望通過?requestAnimationFrame
?收集這一幀內(nèi)的所有事件,在這一幀結(jié)束后,再統(tǒng)一執(zhí)行一次變更檢測(cè)。requestAnimationFrame
?執(zhí)行的返回值會(huì)賦值給?lastRequestAnimationFrameId
,這樣,在接下來代碼每次進(jìn)入到代碼 1 處的時(shí)候,函數(shù)會(huì)直接返回。updateMicroTaskStatus
?被用來更新微任務(wù)狀態(tài)的。那么這里執(zhí)行之后,狀態(tài)值中的 hasPendingMicrotasks 會(huì)變?yōu)?true。這里這么做是為了收集 Event 的時(shí)候可以阻塞微任務(wù)觸發(fā)變更檢測(cè),這么做的原因是為了確保 Event 事件的執(zhí)行順序不會(huì)被微任務(wù)打亂。這里要詳細(xì)介紹又會(huì)有很大篇幅,感興趣的可以自己看下這個(gè)?issue;不想關(guān)注的可以先跳過這里。

當(dāng)當(dāng)前幀執(zhí)行完畢、下一幀要執(zhí)行的時(shí)候會(huì)調(diào)用一次?
checkStable
?函數(shù)。這個(gè)函數(shù)在前面講過,它是觸發(fā) Angular 變更檢測(cè)的關(guān)鍵。通過執(zhí)行該方法,Angular 會(huì)通過 ApplicationRef 執(zhí)行變更檢測(cè)動(dòng)作。
傳送門

再見 ApplicationRef
上一節(jié)中,我們講過一點(diǎn) ApplicationRef 相關(guān)的知識(shí),這一次,我們重點(diǎn)看下 ApplicationRef 跟變更檢測(cè)相關(guān)的代碼。
變更檢測(cè)
前面說到,NgZone 在?checkStable
?中,如果發(fā)現(xiàn)當(dāng)前已經(jīng)沒有待執(zhí)行的任務(wù)的時(shí)候,會(huì)觸發(fā)一個(gè)?onMicrotaskEmpty
?事件。在這里,這個(gè)事件會(huì)被 ApplicationRef 所捕獲。捕獲后,會(huì)執(zhí)行?ApplicationRef.tick
,而這個(gè) tick 就是變更檢測(cè)的入口。
傳送門
tick
在?tick
?方法中,我們可以看到 ApplicationRef 通過調(diào)用視圖的?detectChanges
?方法,讓組件完成自上而下的變更檢測(cè)。上一篇文章中,我們介紹過一些手動(dòng)執(zhí)行變更檢測(cè)的方法,其中有提到過?ChangeDetectorRef.detectChanges()
?這個(gè)方法。這個(gè)方法可以對(duì)當(dāng)前組件以及當(dāng)前組件的子組件進(jìn)行進(jìn)行變更檢測(cè)。那么這里看到的?view.detectChanges()
?跟?ChangeDetectorRef.detectChanges()
?又有什么關(guān)系?
其實(shí)從_views
?類型的繼承鏈可以發(fā)現(xiàn),_views
?的類型?InternalViewRef
?繼承自?ViewRef
,ViewRef
?又繼承自?ChangeDetectorRef
。所以調(diào)用?view.detectChanges()
?就相當(dāng)于調(diào)用了?ChangeDetectorRef.detectChanges()
,從而完成一次自上而下的變更檢測(cè)。
傳送門
以上就是 NgZone 和 ApplicationRef 之間的配合關(guān)系。我們整體再回顧一下整個(gè)系列課程的內(nèi)容。zone.js 通過 Monkey Patch 對(duì)所有異步方法進(jìn)行打包;打包后的異步方法被植入了很多勾子函數(shù),而這些勾子函數(shù)可以被 zone.js 的上下文檢測(cè)到,從而完成對(duì)異步任務(wù)的監(jiān)控。
NgZone 是對(duì) zone.js 的一個(gè)使用案例,NgZone 通過維護(hù) InnerZone 和 OuterZone 兩個(gè) Zone 實(shí)現(xiàn)了對(duì) Angular 應(yīng)用中的異步任務(wù)的監(jiān)控和去監(jiān)控。NgZone 同時(shí)在內(nèi)部也維護(hù)了幾個(gè)對(duì)異步任務(wù)監(jiān)控的狀態(tài)信息,通過這些信息實(shí)現(xiàn)了和 ApplicationRef 之間的 “通信”,最終由 ApplicationRef 完成對(duì) Angular 應(yīng)用的監(jiān)控。
本文小結(jié)
到這里,今天的內(nèi)容就介紹的差不多了。最后,這里還需要像讀者說明一點(diǎn),在 NgZone 中跟蹤 Task 的運(yùn)行是一件比較難的事情,本文所有這些 Task 的舉例其實(shí)都是理想化的。比如說,在舉例 setTimeout 的時(shí)候,你會(huì)發(fā)現(xiàn)當(dāng)你想在 Angular 應(yīng)用中對(duì)異步 Task 跟蹤的時(shí)候,會(huì)有很多其它 Task 同時(shí)在執(zhí)行著,這些 Task 經(jīng)常會(huì)在你調(diào)試跟蹤的時(shí)候?qū)δ阈纬?“干擾”。所以,本文的這些舉例只是希望讓大家看過后,能大致對(duì)每種不同任務(wù)在 NgZone 中流程有個(gè)認(rèn)識(shí),而真實(shí)的過程會(huì)遠(yuǎn)比我今天講的內(nèi)容復(fù)雜的多。這同時(shí)也從側(cè)面反映出,zone.js 默默對(duì) Angular 作出多大的貢獻(xiàn)。
大完結(jié)

本系列分享歷時(shí)將近 1 個(gè)多月,加上前期的一些分析和總結(jié),我個(gè)人大概持續(xù)關(guān)注 zone.js 有兩個(gè)多月了。最后的最后,我也分享幾點(diǎn)個(gè)人感受:
有人說 zone.js 是暴力美學(xué),我個(gè)人感覺可能美的地方更多一些吧。作為 Angular 變更檢測(cè)的核心,Angular 的變更檢測(cè)在三大框架中是獨(dú)一份的存在。我覺得比起其它兩個(gè)通過數(shù)據(jù)劫持和虛擬 Dom 的方式進(jìn)行數(shù)據(jù)綁定的方式,zone.js 顯得還是要溫柔一些的。畢竟數(shù)據(jù)劫持是直接 “污染” 了數(shù)據(jù)的,而 zone.js “改造” 的是工具。我沒法說誰更好,只是個(gè)人更偏向于后者。
截止到現(xiàn)在,我個(gè)人也沒有完全看完 zone.js 的源碼,但是我希望我會(huì)在后續(xù)的工作中持續(xù)關(guān)注這個(gè)產(chǎn)品。同時(shí)我也看到 JiaLi(zone.js 作者)為了他的作品不斷地對(duì) zone.js 進(jìn)行改進(jìn)。所以,請(qǐng)他加油,我希望 zone.js 可以越來越好!不過話說回來,JiaLi 想在 Angular 社區(qū)完成一個(gè) PR 是不是太難了點(diǎn)啊。我看了他好多的修改,經(jīng)常要等好久才能審核通過,有點(diǎn)心疼他。??
其實(shí)最開始的時(shí)候,我只是想自己學(xué)學(xué) zone.js 的,并沒有規(guī)劃這個(gè)系列分享。但是,我在學(xué)習(xí)源碼的時(shí)候,苦于能找到的資料太舊又太少,所以就準(zhǔn)備自己寫一個(gè)有史以來最通俗、最全面、也最適合中國人學(xué)的 zone.js 材料。當(dāng)然,前兩個(gè) “最” 我可能還不配;但是第三個(gè)最,我覺得還是可以搏一搏的?。
OpenTiny Vue 招募貢獻(xiàn)者啦!
OpenTiny Vue 正在招募社區(qū)貢獻(xiàn)者,歡迎加入我們??
你可以通過以下方式參與貢獻(xiàn):
在?issue?列表中選擇自己喜歡的任務(wù)
閱讀貢獻(xiàn)者指南,開始參與貢獻(xiàn)
你可以根據(jù)自己的喜好認(rèn)領(lǐng)以下類型的任務(wù):
編寫單元測(cè)試
修復(fù)組件缺陷
為組件添加新特性
完善組件的文檔
如何貢獻(xiàn)單元測(cè)試:
在?
packages/vue
?目錄下搜索?it.todo
?關(guān)鍵字,找到待補(bǔ)充的單元測(cè)試按照以上指南編寫組件單元測(cè)試
執(zhí)行單個(gè)組件的單元測(cè)試:
pnpm test:unit3 button
如果你是一位經(jīng)驗(yàn)豐富的開發(fā)者,想接受一些有挑戰(zhàn)的任務(wù),可以考慮以下任務(wù):
? [Feature]: 希望提供 Skeleton 骨架屏組件
? [Feature]: 希望提供 Divider 分割線組件
??[Feature]: tree 樹形控件能增加虛擬滾動(dòng)功能
??[Feature]: 增加視頻播放組件
??[Feature]: 增加思維導(dǎo)圖組件
??[Feature]: 添加類似飛書的多維表格組件
??[Feature]: 添加到 unplugin-vue-components
??[Feature]: 兼容 formily
參與 OpenTiny 開源社區(qū)貢獻(xiàn),你將收獲:
直接的價(jià)值:
通過參與一個(gè)實(shí)際的跨端、跨框架組件庫項(xiàng)目,學(xué)習(xí)最新的?
Vite
+Vue3
+TypeScript
+Vitest
?技術(shù)學(xué)習(xí)從 0 到 1 搭建一個(gè)自己的組件庫的整套流程和方法論,包括組件庫工程化、組件的設(shè)計(jì)和開發(fā)等
為自己的簡歷和職業(yè)生涯添彩,參與過優(yōu)秀的開源項(xiàng)目,這本身就是受面試官青睞的亮點(diǎn)
結(jié)識(shí)一群優(yōu)秀的、熱愛學(xué)習(xí)、熱愛開源的小伙伴,大家一起打造一個(gè)偉大的產(chǎn)品
長遠(yuǎn)的價(jià)值:
打造個(gè)人品牌,提升個(gè)人影響力
培養(yǎng)良好的編碼習(xí)慣
獲得華為云 OpenTiny 團(tuán)隊(duì)的榮譽(yù)和定制小禮物
受邀參加各類技術(shù)大會(huì)
成為 PMC 和 Committer 之后還能參與 OpenTiny 整個(gè)開源生態(tài)的決策和長遠(yuǎn)規(guī)劃,培養(yǎng)自己的管理和規(guī)劃能力
未來有更多機(jī)會(huì)和可能
其他說明
OpenTiny 是一套企業(yè)級(jí)組件庫解決方案,適配 PC 端 / 移動(dòng)端等多端,涵蓋 Vue2 / Vue3 / Angular 多技術(shù)棧,擁有主題配置系統(tǒng) / 中后臺(tái)模板 / CLI 命令行等效率提升工具,可幫助開發(fā)者高效開發(fā) Web 應(yīng)用。
核心亮點(diǎn):
跨端跨框架:?使用 Renderless 無渲染組件設(shè)計(jì)架構(gòu),實(shí)現(xiàn)了一套代碼同時(shí)支持 Vue2 / Vue3,PC / Mobile 端,并支持函數(shù)級(jí)別的邏輯定制和全模板替換,靈活性好、二次開發(fā)能力強(qiáng)
組件豐富:PC 端有 100 + 組件,移動(dòng)端有 30 + 組件,包含高頻組件 Table、Tree、Select 等,內(nèi)置虛擬滾動(dòng),保證大數(shù)據(jù)場(chǎng)景下的流暢體驗(yàn),除了業(yè)界常見組件之外,我們還提供了一些獨(dú)有的特色組件,如:Split 面板分割器、IpAddress IP 地址輸入框、Calendar 日歷、Crop 圖片裁切等
配置式組件:?組件支持模板式和配置式兩種使用方式,適合低代碼平臺(tái),目前團(tuán)隊(duì)已經(jīng)將 OpenTiny 集成到內(nèi)部的低代碼平臺(tái),針對(duì)低碼平臺(tái)做了大量優(yōu)化
周邊生態(tài)齊全:?提供了基于 Angular + TypeScript 的 TinyNG 組件庫,提供包含 10+ 實(shí)用功能、20+ 典型頁面的 TinyPro 中后臺(tái)模板,提供覆蓋前端開發(fā)全流程的 TinyCLI 工程化工具,提供強(qiáng)大的在線主題配置平臺(tái) TinyTheme
歡迎加入 OpenTiny 開源社區(qū)。添加微信小助手:opentiny-official 一起參與交流前端技術(shù)~
OpenTiny?官網(wǎng):opentiny.design/
OpenTiny 代碼倉庫:https://github.com/opentiny/
Vue 組件庫:opentiny.design/tiny-vue
Angular 組件庫:opentiny.design/tiny-ng
歡迎進(jìn)入代碼倉庫 Star??TinyVue、TinyNG、TinyCLI~
如果你也想要共建,可以進(jìn)入代碼倉庫,找到?good first issue
?標(biāo)簽,一起參與開源貢獻(xiàn)~
往期文章推薦
必不可少的 UI 組件一 —— 組件的基礎(chǔ)知識(shí)
OpenTiny Vue 3.10.0 版本發(fā)布:組件 Demo 支持 Composition 寫法,新增 4 個(gè)新組件
前端 Vuer,請(qǐng)收好這份《Vue 組件單元測(cè)試》寶典
OpenTiny 前端組件庫正式開源啦!面向未來,為開發(fā)者而生
從自研走向開源的 TinyVue 組件庫
我要做開源,提交我的第一個(gè) PR