【喂饭式调试react源码】上手调试源码探究jsx原理

484 阅读6分钟

🧑‍💻 写在开头

点赞 + 收藏 === 学会🤣🤣🤣

上一篇【react原理实践】使用babel手搓探索下jsx的原理 我们简单讲解了下jsx语法、基本使用、jsx的转换流程、jsx为什么被提到了运行时、和一些简单的源码片段,并使用babel插件简单模拟了下jsx的转换流程。本篇我们将搭建react调试环境,并直接使用源码探究jsx的转换流程。

调试效果

2024-04-24 22.32.32.gif

🥑 你能学到什么?

注意本篇调试的是react18.2.0,使用的是node16 !!!

希望在你阅读本篇文章之后,不会觉得浪费了时间。如果你跟着读下来,你将会学到:

  • react调试环境搭建原理,这里并不会手把手教你搭建,因为很浪费时间,如果是想要学习源码,最好的方式是有一个搭建好的调试环境,这里我已经搭建完成了,clone之后install即可享用
  • 如何在vscode直接调试react源码,对应到源码目录
  • jsx在源码中的转换流程

✍️系列文章react实现原理系列

🥝 一、简单介绍下react源码调试环境搭建的原理

1.调试源码的两种姿势

一般是有两种调试方式,这里我们主要介绍下第二种的原理

  • 调试打包后的react
  • 调试react源码,利用Webpack Alias 的原理

2.Webpack Alias 的原理

原理其实也很简单就是,webpack 的alias配置允许你定义一个别名,它是一个用于替代模块路径的简短字符串。当你在代码中导入一个模块时,如 import something from 'my-module/something',webpack 解析器会根据配置查找该模块,

  • 如果匹配,webpack 就会将该路径替换成 alias 对应的路径,然后再继续正常的模块解析过程。
  • 没有 alias 的情况下,它才会从 node_modules 目录或者相对路径中寻找这个模块。

基于这个原理你可以将react的源码拉去到项目中你想要的位置,我搭建好的是放置在src/react下,然后yarn start将项目跑起来,去解决编译过程中产生的bug,最终搭建完成整个调试环境。

alias: {
    // !!! 更改后的配置
    react: path.join(paths.appSrc, 'react/packages/react'),
    'react-dom': path.join(paths.appSrc, 'react/packages/react-dom'),
    shared: path.join(paths.appSrc, 'react/packages/shared'),
    'react-reconciler': path.join(paths.appSrc, 'react/packages/react-reconciler')
},

image.png

🍑 二、实操

1.在vscode进入调试流程

这里默认你已经clone项目了,项目里面已经配置好了vscode的调试文件,这里我们直接将项目跑起来yarn start

image.png

我们在app.js中打一个断点,行数左侧点一下,就有一个小红点。

image.png

我们切换到运行和调试,点击调试,进入vscode调试模式,代码会停止在我们打断点的地方

image.png

image.png

2.进入jsx转换流程

我们连续点击三次单步调试,注意看,我们这里是可以直接进入到react目录中的jsxWithValidation函数,很爽吧,如果你看过上一篇 文章的话,这个jsx校验函数的入参你会很眼熟。 image.png

进入到这里之后,我们折叠掉一些其他的校验代码,观察下这个函数,他的关键其实是jsxDev这个函数,入参也很眼熟吧。

image.png

command点击进入这个jsxDev函数,看到这个函数跟我们上一篇文章介绍的createElement处理逻辑是一致的,都是格式化数据符合ReactElement入参

image.png

我们command点击进入这个ReactElement函数,就是在构造ReactElement数据结构,可以打印看看,或者直接调试到这个位置,在vscode中查看,一个深度优先的生成顺序

image.png

image.png

我们再打印下app返回的ReactElement Tree

{
    "type": "div",
    "key": null,
    "ref": null,
    "props": {
        "className": "App",
        "children": {
            "type": "header",
            "key": null,
            "ref": null,
            "props": {
                "className": "App-header",
                "children": [
                    {
                        "type": "img",
                        "key": null,
                        "ref": null,
                        "props": {
                            "src": "/static/media/logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg",
                            "className": "App-logo",
                            "alt": "logo"
                        },
                        "_owner": null,
                        "_store": {}
                    },
                    {
                        "type": "p",
                        "key": null,
                        "ref": null,
                        "props": {
                            "children": [
                                "Edit ",
                                {
                                    "type": "code",
                                    "key": null,
                                    "ref": null,
                                    "props": {
                                        "children": "src/App.js"
                                    },
                                    "_owner": null,
                                    "_store": {}
                                },
                                " and save to reload."
                            ]
                        },
                        "_owner": null,
                        "_store": {}
                    },
                    {
                        "type": "a",
                        "key": null,
                        "ref": null,
                        "props": {
                            "className": "App-link",
                            "href": "https://reactjs.org",
                            "target": "_blank",
                            "rel": "noopener noreferrer",
                            "children": "Learn React"
                        },
                        "_owner": null,
                        "_store": {}
                    }
                ]
            },
            "_owner": null,
            "_store": {}
        }
    },
    "_owner": null,
    "_store": {}
}

image.png

这就是jsx整体在源码中的转换流程了。

🍎 推荐阅读

工程化系列

本系列是一个从0到1的实现过程,如果您有耐心跟着实现,您可以实现一个完整的react18 + ts5 + webpack5 + 代码质量&代码风格检测&自动修复 + storybook8 + rollup + git action实现的一个完整的组件库模板项目。如果您不打算自己配置,组件库仓库切换到rollup_comp分支即是完整的项目,当前实现已经足够个人使用,后续我们会新增webpack5优化、按需加载组件、实现一些常见的组件封装:包括但不限于拖拽排序、瀑布流、穿梭框、弹窗等

面试手写系列

react实现原理系列

其他

🍋 写在最后

如果您看到这里了,并且觉得这篇文章对您有所帮助,希望您能够点赞👍和收藏⭐支持一下作者🙇🙇🙇,感谢🍺🍺!如果文中有任何不准确之处,也欢迎您指正,共同进步。感谢您的阅读,期待您的点赞👍和收藏⭐!

感兴趣的同学可以关注下我的公众号ObjectX前端实验室

🌟 少走弯路 | ObjectX前端实验室 🛠️「精选资源|实战经验|技术洞见」