react源碼學習環境搭建

前言

閱讀源碼時,有許多變量在程序運行過程中不斷的產生,其中存放着什麼東西,一直是一個比較頭疼的問題。不停的推導增加了驗算的負擔,隨着代碼逐漸的深入,也會產生一定的記憶負擔。如果靠腦袋去記,簡單點的代碼還好。複雜的代碼。。。你懂的。
隨着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,
    },
},
  • 註釋掉webpackmodule.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語法樹的分析&替換,去通過工具處理了錯誤,這個思路值得我們學習借鑑。

已經搭建好的項目

點我跳轉
這個倉庫是已經配好的環境,安裝完依賴包就可以開始。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章