閱讀源碼時,有許多變量在程序運行過程當中不斷的產生,其中存放着什麼東西,一直是一個比較頭疼的問題。不停的推導增長了驗算的負擔,隨着代碼逐漸的深刻,也會產生必定的記憶負擔。若是靠腦殼去記,簡單點的代碼還好。複雜的代碼。。。你懂的。
隨着react被普遍使用,不少人會好奇react是怎麼實現的。會有一探源碼的想法。若是直接閱讀react.development.js是很簡單,頁面引入就行了。可是react.development.js終因而通過編譯工具編譯過的代碼,不少的代碼看起來並不直觀。理想的狀況是直接引用源文件,也就是github上react倉庫中,packages目錄下的代碼,直接閱讀es6的代碼。
可是es6代碼瀏覽器支持並不友好。因此須要配置webpack打包成es5。同時須要配上sourceMap。這樣,既可讓源碼跑在瀏覽器環境,也能夠直接讀es6的代碼,並且能夠隨時打斷點,查看變量裏保存的值。html
那麼,閒言少敘,開始本章的主題。node
在配置調試環境的過程當中,參考了許多相關資料,這裏先列出來,感興趣的同窗能夠參考。react
主流程相關的資料,點擊跳轉,同時致敬如下用戶webpack
主流程無關可是收益頗豐的資料git
本人所在測試環境爲mac,其餘環境相似,調試版本React Version 16.9.0
須要準備的一些環境: Node/npm/create-react-app/git。
ps:本文是對參考資料的梳理以及優化,力求言簡意賅。es6
git@github.com:pws019/react.git
).npx create-react-app my-app
(利用create-react-app
建立本身的demo項目)cd my-app
(進入上一步建立出來的my-app目錄)yarn run eject
(將webpack的配置提取出來,執行完後項目中會多一個config文件夾,存放webpack相關腳本)src
目錄,git clone git@github.com:pws019/react.git(替換爲你剛fork的react路徑)
/config/webpack.config.js
中的resolve
選項增長alias
以下(爲了讓項目中的引用的react是源碼包裏的react):({ xxxx: 'xxx', resolve: { alias: { // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // 'react-native': 'react-native-web', 'react': path.resolve(__dirname, '../src/react/packages/react'), 'react-dom': path.resolve(__dirname, '../src/react/packages/react-dom'), 'legacy-events': path.resolve(__dirname, '../src/react/packages/legacy-events'), 'shared': path.resolve(__dirname, '../src/react/packages/shared'), 'react-reconciler': path.resolve(__dirname, '../src/react/packages/react-reconciler'), // 'react-events': path.resolve(__dirname, '../src/react/packages/events'), // 'scheduler': path.resolve(__dirname, '../src/react/packages/scheduler'), }, }, xxxx: 'xxx', })
devtool
的值改成source-map
(爲了讓打出的包有sourcemap)/config/env.js
中的stringifed
對象增長屬性:const stringified = { 'xxxx': 'xxx', "__DEV__": true, "__PROFILE__": true, "__UMD__": true };
flow
這個東東作類型檢查,執行yarn add @babel/plugin-transform-flow-strip-types -D
,安裝對應的babel插件忽略flow的類型檢查,而且在webpack的babel-loader
中增長該插件{ test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { customize: require.resolve( 'babel-preset-react-app/webpack-overrides' ), plugins: [ [ require.resolve('babel-plugin-named-asset-import'), { loaderMap: { svg: { ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]', }, }, }, ], [require.resolve('@babel/plugin-transform-flow-strip-types')] //*************這一行是新加的 ], // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, cacheCompression: isEnvProduction, compact: isEnvProduction, }, },
webpack
中module.rules[1]
,也就是eslint的配置,由於我也沒搞明白,怎麼配,總報錯。這時候執行npm start
啓動項目,會發現報錯,主要有三處,依次解決之。github
/src/react/packages/react-reconciler/src/ReactFiberHostConfig.js
。註釋中說明,這塊還須要根據環境去導出HostConfig。export * from './forks/ReactFiberHostConfig.dom';
/src/react/packages/shared/ReactSharedInternals.js
。react此時未export內容,直接從ReactSharedInternals拿值// import React from 'react'; // const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; import ReactSharedInternals from '../react/src/ReactSharedInternals';
src/react/packages/shared/invariant
中的函數,直接拋錯,這裏糾結了很久才找到解法,改下這個函數的內容爲export default function invariant(condition, format, a, b, c, d, e, f) { if(condition) return; throw new Error( 'Internal React error: invariant() is meant to be replaced at compile ' + 'time. There is no runtime version.', ); }
配這個環境的時候,github用戶nannongrousong
的那篇文章給了我不少幫助,基本是按着他的配的,可是在最後,老是在react-dom的checkReact中總報錯,這個問題困擾了我好久。web
後來忽然想起來,官方的文檔裏提到當 invariant 判別條件爲 false 時,會將 invariant 的信息做爲錯誤拋出,而我調試的那個地方,值爲true依然報錯了。npm
後來查看了github用戶nannongrousong
提供的調試環境成品庫,主要差距就是src/react/packages/shared/invariant
裏函數的實現。json
後來我去翻了該文件的commit history
,發現了這個文件的改動歷史,發現是因爲有同窗想減小包的大小,更好的歸攏錯誤提示,而吧這裏抽出來,由自動化工具去替換。
感興趣的能夠看下這裏,
經過這裏的相關代碼,能夠看到,他是利用ast語法樹的分析&替換,去經過工具處理了錯誤,這個思路值得咱們學習借鑑。
點我跳轉這個倉庫是已經配好的環境,安裝完依賴包就能夠開始。