react源碼學習環境搭建

前言

閱讀源碼時,有許多變量在程序運行過程當中不斷的產生,其中存放着什麼東西,一直是一個比較頭疼的問題。不停的推導增長了驗算的負擔,隨着代碼逐漸的深刻,也會產生必定的記憶負擔。若是靠腦殼去記,簡單點的代碼還好。複雜的代碼。。。你懂的。
隨着react被普遍使用,不少人會好奇react是怎麼實現的。會有一探源碼的想法。若是直接閱讀react.development.js是很簡單,頁面引入就行了。可是react.development.js終因而通過編譯工具編譯過的代碼,不少的代碼看起來並不直觀。理想的狀況是直接引用源文件,也就是github上react倉庫中,packages目錄下的代碼,直接閱讀es6的代碼。
可是es6代碼瀏覽器支持並不友好。因此須要配置webpack打包成es5。同時須要配上sourceMap。這樣,既可讓源碼跑在瀏覽器環境,也能夠直接讀es6的代碼,並且能夠隨時打斷點,查看變量裏保存的值。html

那麼,閒言少敘,開始本章的主題。node

參考資料

在配置調試環境的過程當中,參考了許多相關資料,這裏先列出來,感興趣的同窗能夠參考。react

正文

本人所在測試環境爲mac,其餘環境相似,調試版本React Version 16.9.0
須要準備的一些環境: Node/npm/create-react-app/git。
ps:本文是對參考資料的梳理以及優化,力求言簡意賅。es6

  • Fork react的倉庫到本身的git上。(爲了方便本身作記錄,fork後本身的git上也會有react,改完後能夠往本身的這個上push,若是是clone,則沒有權限push,例如我fork出來的地址爲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
};
  • 因爲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啓動項目,會發現報錯,主要有三處,依次解決之。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';
    • 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中總報錯,這個問題困擾了我好久。web

後來忽然想起來,官方的文檔裏提到當 invariant 判別條件爲 false 時,會將 invariant 的信息做爲錯誤拋出,而我調試的那個地方,值爲true依然報錯了。npm

後來查看了github用戶nannongrousong提供的調試環境成品庫,主要差距就是src/react/packages/shared/invariant裏函數的實現。json

後來我去翻了該文件的commit history,發現了這個文件的改動歷史,發現是因爲有同窗想減小包的大小,更好的歸攏錯誤提示,而吧這裏抽出來,由自動化工具去替換。

感興趣的能夠看下這裏,
經過這裏的相關代碼,能夠看到,他是利用ast語法樹的分析&替換,去經過工具處理了錯誤,這個思路值得咱們學習借鑑。

已經搭建好的項目

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

相關文章
相關標籤/搜索