每次都信誓旦旦的給本身立下要好好學習react源碼的flag,結果都是由於某個地方卡住了,或是其餘緣由沒看多少就放棄了。此次又給本身立個flag-堅持看完react源碼。爲了敦促本身,特開設這樣一個專欄來記錄本身的學習歷程,這意味着這個專欄的文章質量並不高,你能夠拿來參考參考,切莫全信,我不想誤人子弟,後面要是學有所成再考慮產出些好點的文章。 要是發現文章中有什麼不當之處,歡迎批評交流。我看的源碼版本是
16.8.2
。
爲了看react源碼,我查找了很多資料,這裏推薦兩個參考資料,我的以爲寫得不錯。javascript
15.6.2
的, 在react16
裏面一些方法找不到了。本篇文章是官方文檔裏邊的一篇文章的翻譯,原文地址。php
這部分將給你介紹下react代碼的基本結構, 代碼約定和它的基本實現.
若是你想爲react貢獻代碼的話, 咱們但願這篇指南能讓你寫代碼更加舒服.html
咱們不推薦將這些約定用在react應用中, 由於這些約定大可能是基於一些歷史緣由存在的, 隨着時間推移可能會發生變化.java
react 幾乎沒有外部依賴. 一般require()
指向的是react本身代碼庫的一個文件. 可是也有一些例外.node
因爲react想要經過庫共享一些諸如Relay
的小工具, 因此存在fbjs repository
, 並且咱們讓他們是同步的. 咱們沒有依賴任何node生態系統下的小模塊, 由於咱們但願facebook的工程師的能能再任何須要的時候修改他們. fbjs中的任何工具都不能被認爲是公共api, 而且他們只是爲Facebook的一些工程使用, 好比react.react
克隆了react的倉庫後你會發如今裏邊有幾個一級目錄.git
packages
目錄包括一些元數據(如package.json)和react庫提供的全部包的源碼(src
的下面), 若是你想修改代碼, src
下面就是你要花時間最多的地方.fixtures
目錄包括了爲貢獻者準備的一些小的react的測試應用build
是react打包輸出的目錄. 他不在代碼庫管理範疇, 可是當你第一次打包後就會生成.文檔是放在和react不一樣的另外一個倉庫管理的.github
還有一些其餘一級目錄, 他們大可能是工具層面的, 在你貢獻代碼時可能不會用到他們能.算法
咱們沒有搞個一級目錄來作單元測試. 咱們把它放在了被測試文件相鄰的被稱爲__tests__
的目錄.npm
舉個例子, 對於setInnerHTML.js
這個文件的測試被放在與他同級的__tests__/setInnerHTML-test.js
這個裏邊.
這個詞不知道怎麼翻譯
react中使用warning
模塊顯示警告信息.
var warning = require('warning'); warning( 2 + 2 === 4, 'Math is not working today.' );
當警告條件是false的時候會展現警告信息
能夠這麼理解, 條件應該指示正常的狀況, 而不是異常的狀況. 就是說第一個參數是true表示的是正常, false是異常.
最好避免使用console取代warnings.
var warning = require('warning'); var didWarnAboutMath = false; if (!didWarnAboutMath) { warning( 2 + 2 === 4, 'Math is not working today.' ); didWarnAboutMath = true; }
警告只會在開發模式被開啓. 生產環境下被去掉了. 若是你想阻止某些代碼塊的執行, 那麼你能夠用invariant
模塊.
var invariant = require('invariant'); invariant( 2 + 2 === 4, 'You shall not pass!' );
當條件爲false時, 這個方法會直接拋出異常.
「Invariant」 就是說這個條件爲真, 你能夠認爲他就是作了個斷言.
保持開發環境和生產環境一致是很重要的, 所以invariant
在生產環境和開發環境均可以拋出異常. 生產環境下的錯誤消息被自動替換成錯誤碼, 以防增長代碼體積.
你可使用__DEV__
這個爲全局變量指定僅僅在開發環境才執行的代碼塊.
他是在編譯過程當中工做的, 他是在commonjs編譯的時候檢查process.env.NODE_ENV !== 'production'
這個值.
單獨編譯的時候, 他在未壓縮版是true, 在壓縮版直接被去掉了.
if (__DEV__) { // 這裏邊的代碼只會帶開發環境執行 }
咱們最近開始引入flow
作靜態類型檢查, 在文件頭的註釋裏標註了@flow
的使用了類型檢查.
咱們接受在現有代碼加入flow類型檢查的pull request (不錯哎, 能夠試着提個pull request哦). Flow的簽名相似下面這樣.
ReactRef.detachRefs = function( instance: ReactInstance, element: ReactElement | string | number | null | false, ): void { // ... }
時機成熟的時候, 新代碼要用Flow 簽名, 你能夠在本地運行yarn flow
用Flow檢查你的代碼.
react在一些模塊使用了動態植入. 可是這個東西不太好, 由於他讓代碼比較難理解了. 他存在的理由是react一開始只把支持dom做爲目標的. 可是後來殺出了個React Native, 他是基於react的, 咱們不得不加入動態植入好讓react native 重載一些行爲.
你可能會看到模塊像下面這樣聲明它的動態依賴
// Dynamically injected var textComponentClass = null; // Relies on dynamically injected value function createInstanceForText(text) { return new textComponentClass(text); } var ReactHostComponent = { createInstanceForText, // Provides an opportunity for dynamic injection injection: { injectTextComponentClass: function(componentClass) { textComponentClass = componentClass; }, }, }; module.exports = ReactHostComponent;
注入的部分沒有以任何方式特殊處理. 可是規定, 它的意思是這個模塊想在運行時有一些依賴(多是平臺特定的)被注入進去.
代碼裏邊有幾個注入的入口. 將來, 咱們將廢棄掉這種動態植入的機制, 方案是在編譯時以靜態方式處理他們.
react是個monorepo
, 他的倉庫包含了多個獨立的包, 所以他們的修改能夠合在一塊兒, 並且issues也能夠放在一個地方.
react的核心是全部頂級api, 包括:
react核心只包括定義組件必要的api, 並不包括reconciliation
算法和平臺特定代碼. React DOM和React Native都使用了他們.
react核心的相關代碼在packages/react
裏邊. npm使用時在react這個包裏邊, 瀏覽器版的是react.js, 他掛載一個被稱爲React的全局變量.
react起初是爲DOM創造的, 可是後臺經過RN被用來支持原生環境了. 這裏介紹加react內部的「renderers」的理念.
「renderers」管理了react樹如何變成平臺可調用的東西.
Renderers也在packages
裏邊
React DOM Renderer
把react 組件渲染進 DOM. 他實現了頂級的ReactDOM APIs
, 在react-dom
這個npm包裏被暴露出來. 瀏覽器版叫react-dom.js, 經過ReactDOM這個全局變量暴露出來.React Native Renderer
把react組件渲染到原生視圖層裏. 他被RN內部使用.React Test Renderer
把react組件渲染成JSON樹, 他被Jest
的一個特性Snapshot Testing
使用, 在react-test-renderer
這個npm包裏可用.另外一個官方惟一支持的渲染器是react-art
, 他曾經是個獨立的庫, 如今被移進來了.
注意
技術上
react-native-renderer
是很薄的一層, 只是用來和RN的實現相互配合, 真正的平臺相關代碼是RN庫裏一些native view.
至關多的渲染器, 如Reat DOM, React Native 須要共享一套邏輯. 尤爲reconciliation算法須要足夠的類似, 以便讓rendering, 自定義組件, 狀態, 生命週期函數和refs能跨平臺工做.
爲了解決這個問題, 不一樣的渲染器共用一些代碼. 咱們把React 中的這個部分叫作"reconciler". 當一個更新好比setState要執行了,Reconcilers就去在組件上調用render(), 而後mounts, updates, 或者unmounts他們.
Reconcilers沒有獨立成包, 由於他如今尚未公共API. 相反, 他僅僅是在渲染器被使用, 好比React DOM , React Native.
Stack Reconciler 是在react15以前實現使用的, 如今已經不用了, 可是下一部分的文檔還會有詳細的介紹.
"Fiber"是爲了解決stack reconciler固有問題和修復長期存在的bug所作的努力, 他從react16開始成爲默認的Reconciler.
他的主要目標是:
你可在這裏和這裏閱讀更多關於Fiber架構的相關信息. 可是React16對他作了封裝, 默認不支持異步特性了.
他的源碼在packages/react-reconciler
裏邊.
react實現了一個對renders透明的事件系統, 這個系統被用於react dom 和react native. 源碼在packages/events
;