為什么 React 源碼不用 TypeScript 來(lái)寫?

周末的,看點(diǎn)輕松的把,之前看過(guò) React 的源碼,比較好奇像 React 這樣龐大的工程為什么沒(méi)有用 TypeScript。
Facebook 工程師 Cat Chen 在知乎上(https://www.zhihu.com/question/378470381/answer/1079675543)從內(nèi)部的角度解答了為什么現(xiàn)在 React 的源代碼用了 Flow 但沒(méi)有用 TypeScript。
Facebook 是一家技術(shù)很厲害的公司,能夠超前做一些外界沒(méi)有的東西,但等外界把這個(gè)東西做出來(lái)了,Facebook 就發(fā)現(xiàn)自己遷移不過(guò)去了,被自己過(guò)去超前做的技術(shù)鎖定了,因?yàn)檫w移成本太高。舉個(gè)例子,在還沒(méi)有 webpack 的時(shí)候 Facebook 就有自己很好的前端構(gòu)建流水線,但 webpack 出來(lái)后 Facebook 無(wú)法遷移到 webpack,甚至無(wú)法輕易把 transpiler 遷移到 Babel。
如果我沒(méi)記錯(cuò)的話,Babel 作者 Sebastian McKenzie 進(jìn)入 Facebook 后做過(guò)一個(gè)項(xiàng)目,就是幫助 Facebook 遷移到 Babel。為什么呢?因?yàn)樵谕饨邕€沒(méi)有 Babel 的時(shí)候,甚至在 Babel 前身 6to5 還沒(méi)出現(xiàn)的時(shí)候,Facebook 內(nèi)部的流水線已經(jīng)有自己的 transpiler,能夠把一部分 ES6 語(yǔ)法轉(zhuǎn)譯為 ES5。當(dāng)時(shí)我們可爽了,在外部根本還沒(méi)意識(shí)到能這樣做事情的時(shí)候,我們已經(jīng)可以隨手寫 ES6 了。但有了 Babel 后,內(nèi)部流水線根本不兼容中間插入 Babel 這一步,所以就需要專門改造這個(gè)流水線才能遷移到 Babel。而且 Facebook 已有的大量代碼的 ES6 寫法是基于內(nèi)部 transpiler 寫的,誰(shuí)能保證遷移到 Babel 后 100% 兼容?遷移到 Babel 后如果編譯出錯(cuò)了,那還能找出來(lái)修復(fù)。如果不出錯(cuò),但實(shí)際執(zhí)行結(jié)果略微不一樣,導(dǎo)致出現(xiàn)線上事故,那怎么辦?
React 在寫的時(shí)候,是基于上述 Facebook 內(nèi)部流水線寫的,所以自然是內(nèi)部有什么工具就依賴什么工具。React 一開始寫的時(shí)候,其實(shí)是沒(méi)有 Babel、TypeScript 和 Flow 的,但有上述內(nèi)部 transpiler,所以就這樣寫了。到后來(lái)有了 Flow,而且要保證依賴于 React 的代碼能夠得到正確的 Flow 類型推斷,自然就加上了 Flow 注釋。此外,內(nèi)部流水線應(yīng)該是從來(lái)沒(méi)做過(guò) TypeScript 支持的,所以估計(jì)就算想用 TypeScript 來(lái)寫也做不到,因?yàn)檫@不是加個(gè) webpack 插件就能支持的。
為什么 Facebook 要做 Flow 而不用 TypeScript?這是一個(gè)規(guī)模的問(wèn)題,對(duì)于大多數(shù)一般人來(lái)說(shuō)沒(méi)有這個(gè)規(guī)模是不可能理解的。我可以舉若干個(gè)故事來(lái)解釋一下什么是規(guī)模,以及為什么大多數(shù)人都沒(méi)有機(jī)會(huì)解決這類問(wèn)題,但非常少數(shù)的超大規(guī)模型公司需要請(qǐng)非常資深的工程師來(lái)解決。
第一個(gè)例子是為什么 Facebook 不能用 TypeScript,因?yàn)?TypeScript 會(huì)把所有源代碼加載到內(nèi)存里進(jìn)行處理。Facebook 用的是 monorepo,也就是一個(gè)單體源代碼庫(kù),不按項(xiàng)目分多個(gè)代碼庫(kù),這使得互相引用的 JavaScript 非常龐大。如果讓 TypeScript 直接跑,TypeScript 的內(nèi)存開銷會(huì)大到連 Facebook 的服務(wù)器都 hold 不住,然后崩潰掉。為此 Facebook 堅(jiān)持做自己的 Flow,Flow 能夠分拆要處理的 JavaScript 然后一部分一部分地處理,解決 Facebook 內(nèi)部的獨(dú)家問(wèn)題。Facebook Flow 團(tuán)隊(duì)的態(tài)度很明確:「這是做給我們自己用的,我們順手開源而已,你們愛(ài)用就用,沒(méi)人逼你們用。我們會(huì)優(yōu)先做內(nèi)部需求,社區(qū)需求不著急做,如果社區(qū)需求跟內(nèi)部需求沖突的必然讓路給內(nèi)部需求。」
第二個(gè)例子是為什么 Facebook 用 Mercurial (hg) 而不用 Git,因?yàn)?Facebook 幾年前每周的 commit 數(shù)量就高達(dá)五位數(shù),現(xiàn)在可能已經(jīng)六位數(shù)了。這導(dǎo)致每天上班跑一次 git pull --rebase 搞不好就 30 分鐘,非常影響生產(chǎn)力,而且一天還可能要跑幾次。既然 Git 是開源的,Facebook 就想要去改 Git 源代碼,改善自己工作流程的生產(chǎn)力。Facebook 主動(dòng)去撩 Git,說(shuō)「我們?cè)敢庳暙I(xiàn)源代碼啊,我們可以優(yōu)化我們?cè)诤醯墓ぷ髁鞒痰男阅?,你們?cè)敢饨邮芪覀兊?Pull Request 嗎?」Git 說(shuō)「滾!這是你們特有的問(wèn)題,正常的 Git 用戶根本沒(méi)有這么高的 commit 流量,不要引入非必要的復(fù)雜度到我們的源代碼里。」于是 Facebook 跑去撩 Mercurial,人家說(shuō)「歡迎來(lái)貢獻(xiàn)代碼」,于是 Facebook 就切換到 Mercurial 了。
第三個(gè)例子是 Facebook 特有的 iOS 開發(fā)環(huán)境優(yōu)化。跟上面的例子相似,如果當(dāng)作一個(gè)普通的 Xcode 項(xiàng)目打開 Facebook 主應(yīng)用的源代碼,編譯一下就 60 分鐘,根本無(wú)法干活。所以 Facebook 做了一些非常專門的優(yōu)化,保證大家如果只是改動(dòng)一兩個(gè)模塊里的代碼時(shí),編譯速度非常快,一下子就能把 Facebook 主應(yīng)用編譯出來(lái),能看到改動(dòng)的效果。Facebook 跟 Apple 的關(guān)系不錯(cuò),就問(wèn) Apple 有沒(méi)有興趣對(duì) Xcode 做一些相關(guān)的優(yōu)化,Apple 當(dāng)然表示沒(méi)興趣。Apple 說(shuō)他們真正看重的是那些一兩個(gè)程序員組成的小開發(fā)者,因?yàn)檫@種類型開發(fā)者寫了絕大部分 App Store 上面的應(yīng)用,包括大部分的高質(zhì)量應(yīng)用。Apple 要優(yōu)先滿足這種類型開發(fā)者的需求,而不是 Facebook 這種「僅此一家」的需求。
舉這三個(gè)例子是為了說(shuō)明這樣一個(gè)事情:Facebook 的很多問(wèn)題是獨(dú)家的,并不是外部看到的那么簡(jiǎn)單的問(wèn)題。有些很多問(wèn)題是因?yàn)橐?guī)模導(dǎo)致的,做不到 Facebook 這樣的規(guī)模根本遇不到這樣的問(wèn)題。你可以想象你自己做項(xiàng)目會(huì)怎么做,但你把這應(yīng)用到 Facebook 這樣的規(guī)模就很可能行不通。
??愛(ài)心三連擊 1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的「點(diǎn)贊,在看」是我創(chuàng)作的動(dòng)力。
2.關(guān)注公眾號(hào)
程序員成長(zhǎng)指北,回復(fù)「1」加入高級(jí)前端交流群!「在這里有好多 前端?開發(fā)者,會(huì)討論?前端 Node 知識(shí),互相學(xué)習(xí)」!3.也可添加微信【ikoala520】,一起成長(zhǎng)。
“在看轉(zhuǎn)發(fā)”是最大的支持
