前言
閱讀源碼時,有許多變量在程序運行過程中不斷的產生,其中存放着什麼東西,一直是一個比較頭疼的問題。不停的推導增加了驗算的負擔,隨着代碼逐漸的深入,也會產生一定的記憶負擔。如果靠腦袋去記,簡單點的代碼還好。複雜的代碼。。。你懂的。
隨着react被廣泛使用,很多人會好奇react是怎麼實現的。會有一探源碼的想法。如果直接閱讀react.development.js是很簡單,頁面引入就好了。但是react.development.js終於是經過編譯工具編譯過的代碼,很多的代碼看起來並不直觀。理想的情況是直接引用源文件,也就是github上react倉庫中,packages目錄下的代碼,直接閱讀es6的代碼。
但是es6代碼瀏覽器支持並不友好。所以需要配置webpack打包成es5。同時需要配上sourceMap。這樣,既可以讓源碼跑在瀏覽器環境,也可以直接讀es6的代碼,而且可以隨時打斷點,查看變量裏保存的值。
那麼,閒言少敘,開始本章的主題。
參考資料
在配置調試環境的過程中,參考了許多相關資料,這裏先列出來,感興趣的同學可以參考。
-
主流程相關的資料,點擊跳轉,同時致敬以下用戶
-
主流程無關但是收益頗豐的資料
正文
本人所在測試環境爲mac,其他環境類似,調試版本React Version 16.9.0
需要準備的一些環境: Node/npm/create-react-app/git。
ps:本文是對參考資料的梳理以及優化,力求言簡意賅。
- Fork react的倉庫到自己的git上。(爲了方便自己做記錄,fork後自己的git上也會有react,改完後可以往自己的這個上push,如果是clone,則沒有權限push,例如我fork出來的地址爲
[email protected]: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 [email protected]: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
};
- 由於react的源碼中採用了
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
啓動項目,會發現報錯,主要有三處,依次解決之。- 修改文件
/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';
- checkReact文件報錯,是由於
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中總報錯,這個問題困擾了我很久。
後來突然想起來,官方的文檔裏提到當 invariant 判別條件爲 false 時,會將 invariant 的信息作爲錯誤拋出,而我調試的那個地方,值爲true依然報錯了。
後來查看了github用戶nannongrousong
提供的調試環境成品庫,主要差距就是src/react/packages/shared/invariant
裏函數的實現。
後來我去翻了該文件的commit history
,發現了這個文件的改動歷史,發現是由於有同學想減少包的大小,更好的歸攏錯誤提示,而吧這裏抽出來,由自動化工具去替換。
感興趣的可以看下這裏,
通過這裏的相關代碼,可以看到,他是利用ast語法樹的分析&替換,去通過工具處理了錯誤,這個思路值得我們學習借鑑。
已經搭建好的項目
點我跳轉
這個倉庫是已經配好的環境,安裝完依賴包就可以開始。